Android实战之app版本更新升级全文章(一)

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

以上就是版本更新接口的调用,具体的rxjava+retrofit请自行学习你真的会用Retrofit2吗?Retrofit2完全教程

附上结果回调监听:

public interface CheckCallBack{//检测成功或者失败的相关接口 void onSuccess(UpdateAppInfo updateInfo); void onError(); }

  • 1

  • 2

  • 3

  • 4

  • 1

  • 2

  • 3

  • 4

具体使用接口的处理:

//网络检查版本是否需要更新 CheckUpdateUtils.checkUpdate("apk", "1.0.0", new CheckUpdateUtils.CheckCallBack() { @Override public void onSuccess(UpdateAppInfo updateInfo) { String isForce=updateInfo.data.getLastForce();//是否需要强制更新 String downUrl= updateInfo.data.getUpdateurl();//apk下载地址 String updateinfo = updateInfo.data.getUpgradeinfo();//apk更新详情 String appName = updateInfo.data.getAppname(); if(isForce.equals("1")&& !TextUtils.isEmpty(updateinfo)){//强制更新 forceUpdate(MainActivity.this,appName,downUrl,updateinfo); }else{//非强制更新 //正常升级 normalUpdate(MainActivity.this,appName,downUrl,updateinfo); } } @Override public void onError() { noneUpdate(MainActivity.this); } });

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

实在不想写网络也好,直接使用假想数据做相关操作如下:

UpdateAppInfo.UpdateInfo info =new UpdateAppInfo.UpdateInfo(); info.setLastForce("1"); info.setAppname("我日你"); info.setUpgradeinfo("whejjefjhrherkjreghgrjrgjjhrh"); info.setUpdateurl("http://releases.b0.upaiyun.com/hoolay.apk"); if(info.getLastForce().equals("1")){//强制更新 forceUpdate(MainActivity.this,info.getAppname(),info.getUpdateurl(),info.getUpgradeinfo()); }else{//非强制更新 //正常升级 normalUpdate(MainActivity.this,info.getAppname(),info.getUpdateurl(),info.getUpgradeinfo()); }

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

更新dialog的使用注意:

private void forceUpdate(final Context context, final String appName, final String downUrl, final String updateinfo) { mDialog = new AlertDialog.Builder(context); mDialog.setTitle(appName+"又更新咯!"); mDialog.setMessage(updateinfo); mDialog.setPositiveButton("立即更新", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (!canDownloadState()) { showDownloadSetting(); return; } // DownLoadApk.download(MainActivity.this,downUrl,updateinfo,appName); AppInnerDownLoder.downLoadApk(MainActivity.this,downUrl,appName); } }).setCancelable(false).create().show(); }

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

上面以强制更新举个例子,因为AlertDialog在不同的版本下面表现的美观度不一致,所以我们需要

import android.support.v7.app.AlertDialog;

  • 1
  • 1

然后显然是不能按返回键取消的,我们需要

.setCancelable(false)

  • 1
  • 1
使用谷歌推荐的DownloadManager实现下载

Android自带的DownloadManager模块来下载,在api level 9之后,我们通过通知栏知道, 该模块属于系统自带, 它已经帮我们处理了下载失败、重新下载等功能。整个下载 过程全部交给系统负责,不需要我们过多的处理。

DownLoadManager.Query:主要用于查询下载信息。

DownLoadManager.Request:主要用于发起一个下载请求。

先看下简单的实现:

创建Request对象的代码如下:

DownloadManager.Request request = new DownloadManager.Request(Uri.parse(apkurl)); //设置在什么网络情况下进行下载 request.setAllowedNetworkTypes(Request.NETWORK_WIFI); //设置通知栏标题 request.setNotificationVisibility(Request.VISIBILITY_VISIBLE); request.setTitle("下载"); request.setDescription("apk正在下载"); request.setAllowedOverRoaming(false); //设置文件存放目录 request.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS, "mydown");

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

取得系统服务后,调用downloadmanager对象的enqueue方法进行下载,此方法返回一个编号用于标示此下载任务:

downManager = (DownloadManager)getSystemService(Context.DOWNLOAD_SERVICE); id= downManager.enqueue(request);

  • 1

  • 2

  • 1

  • 2

这里我们可以看下request的一些属性:

addRequestHeader(String header,String value):添加网络下载请求的http头信息 allowScanningByMediaScanner():用于设置是否允许本MediaScanner扫描。 setAllowedNetworkTypes(int flags):设置用于下载时的网络类型,默认任何网络都可以下载,提供的网络常量有:NETWORK_BLUETOOTH、NETWORK_MOBILE、NETWORK_WIFI。 setAllowedOverRoaming(Boolean allowed):用于设置漫游状态下是否可以下载 setNotificationVisibility(int visibility):用于设置下载时时候在状态栏显示通知信息 setTitle(CharSequence):设置Notification的title信息 setDescription(CharSequence):设置Notification的message信息 setDestinationInExternalFilesDir、setDestinationInExternalPublicDir、 setDestinationUri等方法用于设置下载文件的存放路径,注意如果将下载文件存放在默认路径,那么在空间不足的情况下系统会将文件删除,所 以使用上述方法设置文件存放目录是十分必要的。

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

具体实现思路:

  1. 我们通过downloaderManager来下载apk,并且本地保存downManager.enqueue(request)返回的id值,并且通过这个id获取apk的下载文件路径和下载的状态,并且通过状态来更新通知栏的显示。
  1. 第一次下载成功,弹出安装界面
如果用户没有点击安装,而是按了返回键,在某个时候,又再次使用了我们的APP
如果下载成功,则判断本地的apk的包名是否和当前程序是相同的,并且本地apk的版本号大于当前程序的版本,如果都满足则直接启动安装程序。

具体代码实现:

文件下载管理的实现,包括创建request和加入队列下载,通过返回的id来获取下载路径和下载状态。

public class FileDownloadManager { private DownloadManager downloadManager; private Context context; private static FileDownloadManager instance; private FileDownloadManager(Context context) { downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); this.context = context.getApplicationContext(); } public static FileDownloadManager getInstance(Context context) { if (instance == null) { instance = new FileDownloadManager(context); } return instance; } /** * @param uri * @param title * @param description * @return download id */ public long startDownload(String uri, String title, String description,String appName) { DownloadManager.Request req = new DownloadManager.Request(Uri.parse(uri)); req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI); //req.setAllowedOverRoaming(false); req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); //设置文件的保存的位置[三种方式] //第一种 //file:///storage/emulated/0/Android/data/your-package/files/Download/update.apk req.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, appName+".apk"); //第二种 //file:///storage/emulated/0/Download/update.apk //req.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "update.apk"); //第三种 自定义文件路径 //req.setDestinationUri() // 设置一些基本显示信息 req.setTitle(title); req.setDescription(description); //req.setMimeType("application/vnd.android.package-archive"); return downloadManager.enqueue(req);//异步 //dm.openDownloadedFile() } /** * 获取文件保存的路径 * * @param downloadId an ID for the download, unique across the system. * This ID is used to make future calls related to this download. * @return file path * @see FileDownloadManager#getDownloadUri(long) */ public String getDownloadPath(long downloadId) { DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId); Cursor c = downloadManager.query(query); if (c != null) { try { if (c.moveToFirst()) { return c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI)); } } finally { c.close(); } } return null; } /** * 获取保存文件的地址 * * @param downloadId an ID for the download, unique across the system. * This ID is used to make future calls related to this download. * @see FileDownloadManager#getDownloadPath(long) */ public Uri getDownloadUri(long downloadId) { return downloadManager.getUriForDownloadedFile(downloadId); } public DownloadManager getDownloadManager() { return downloadManager; } /** * 获取下载状态 * * @param downloadId an ID for the download, unique across the system. * This ID is used to make future calls related to this download. * @return int * @see DownloadManager#STATUS_PENDING * @see DownloadManager#STATUS_PAUSED * @see DownloadManager#STATUS_RUNNING * @see DownloadManager#STATUS_SUCCESSFUL * @see DownloadManager#STATUS_FAILED */ public int getDownloadStatus(long downloadId) { DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId); Cursor c = downloadManager.query(query); if (c != null) { try { if (c.moveToFirst()) { return c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS)); } } finally { c.close(); } } return -1; } }

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

  • 50

  • 51

  • 52

  • 53

  • 54

  • 55

  • 56

  • 57

  • 58

  • 59

  • 60

  • 61

  • 62

  • 63

  • 64

  • 65

  • 66

  • 67

  • 68

  • 69

  • 70

  • 71

  • 72

  • 73

  • 74

  • 75

  • 76

  • 77

  • 78

  • 79

  • 80

  • 81

  • 82

  • 83

  • 84

  • 85

  • 86

  • 87

  • 88

  • 89

  • 90

  • 91

  • 92

  • 93

  • 94

  • 95

  • 96

  • 97

  • 98

  • 99

  • 100

  • 101

  • 102

  • 103

  • 104

  • 105

  • 106

  • 107

  • 108

  • 109

  • 110

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

  • 50

  • 51

  • 52

  • 53

  • 54

  • 55

  • 56

  • 57

  • 58

  • 59

  • 60

  • 61

  • 62

  • 63

  • 64

  • 65

  • 66

  • 67

  • 68

  • 69

  • 70

  • 71

  • 72

  • 73

  • 74

  • 75

  • 76

  • 77

  • 78

  • 79

  • 80

  • 81

  • 82

  • 83

  • 84

  • 85

  • 86

  • 87

  • 88

  • 89

  • 90

  • 91

  • 92

  • 93

  • 94

  • 95

  • 96

  • 97

  • 98

  • 99

  • 100

  • 101

  • 102

  • 103

  • 104

  • 105

  • 106

  • 107

  • 108

  • 109

  • 110

app的检测安装的实现:

public class DownLoadApk { public static final String TAG = DownLoadApk.class.getSimpleName(); public static void download(Context context, String url, String title,final String appName) { // 获取存储ID SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); long downloadId =sp.getLong(DownloadManager.EXTRA_DOWNLOAD_ID,-1L); if (downloadId != -1L) { FileDownloadManager fdm = FileDownloadManager.getInstance(context); int status = fdm.getDownloadStatus(downloadId); if (status == DownloadManager.STATUS_SUCCESSFUL) { //启动更新界面 Uri uri = fdm.getDownloadUri(downloadId); if (uri != null) { if (compare(getApkInfo(context, uri.getPath()), context)) { startInstall(context, uri); return; } else { fdm.getDownloadManager().remove(downloadId); } } start(context, url, title,appName); } else if (status == DownloadManager.STATUS_FAILED) { start(context, url, title,appName); } else { Log.d(TAG, "apk is already downloading"); } } else { start(context, url, title,appName); } } private static void start(Context context, String url, String title,String appName) { long id = FileDownloadManager.getInstance(context).startDownload(url, title, "下载完成后点击打开",appName); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); sp.edit().putLong(DownloadManager.EXTRA_DOWNLOAD_ID,id).commit(); Log.d(TAG, "apk start download " + id); } public static void startInstall(Context context, Uri uri) { Intent install = new Intent(Intent.ACTION_VIEW); install.setDataAndType(uri, "application/vnd.android.package-archive"); install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(install); } /** * 获取apk程序信息[packageName,versionName...] * * @param context Context * @param path apk path */ private static PackageInfo getApkInfo(Context context, String path) { PackageManager pm = context.getPackageManager(); PackageInfo info = pm.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES); if (info != null) { return info; } return null; } /** * 下载的apk和当前程序版本比较 * * @param apkInfo apk file's packageInfo * @param context Context * @return 如果当前应用版本小于apk的版本则返回true */ private static boolean compare(PackageInfo apkInfo, Context context) { if (apkInfo == null) { return false; } String localPackage = context.getPackageName(); if (apkInfo.packageName.equals(localPackage)) { try { PackageInfo packageInfo = context.getPackageManager().getPackageInfo(localPackage, 0); if (apkInfo.versionCode > packageInfo.versionCode) { return true; } } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } return false; } }

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

  • 50

  • 51

  • 52

  • 53

  • 54

  • 55

  • 56

  • 57

  • 58

  • 59

  • 60

  • 61

  • 62

  • 63

  • 64

  • 65

  • 66

  • 67

  • 68

  • 69

  • 70

  • 71

  • 72

  • 73

  • 74

  • 75

  • 76

  • 77

  • 78

  • 79

  • 80

  • 81

  • 82

  • 83

  • 84

  • 85

  • 86

  • 87

  • 88

  • 89

  • 90

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

  • 50

  • 51

  • 52

  • 53

  • 54

  • 55

  • 56

  • 57

  • 58

  • 59

  • 60

  • 61

  • 62

  • 63

  • 64

  • 65

  • 66

  • 67

  • 68

  • 69

  • 70

  • 71

  • 72

  • 73

  • 74

  • 75

  • 76

  • 77

  • 78

  • 79

  • 80

  • 81

  • 82

  • 83

  • 84

  • 85

  • 86

  • 87

  • 88

  • 89

  • 90

上面的代码可知:我们通过获取当前app的信息来比较是否需要下载和是否立即安装。第一次下载把downloadId保存到本地,用户下次进来的时候,取出保存的downloadId,然后通过downloadId来获取下载的状态信息。如果下载失败,则重新下载并且把downloadId存起来。如果下载成功,则判断本地的apk的包名是否和当前程序是相同的,并且本地apk的版本号大于当前程序的版本,如果都满足则直接启动安装程序。

监听app是否安装完成

public class ApkInstallReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)){ long downloadApkId =intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); installApk(context, downloadApkId); } } /** * 安装apk */ private void installApk(Context context,long downloadApkId) { // 获取存储ID SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); long downId =sp.getLong(DownloadManager.EXTRA_DOWNLOAD_ID,-1L); if(downloadApkId == downId){ DownloadManager downManager= (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); Uri downloadFileUri = downManager.getUriForDownloadedFile(downloadApkId); if (downloadFileUri != null) { Intent install= new Intent(Intent.ACTION_VIEW); install.setDataAndType(downloadFileUri, "application/vnd.android.package-archive"); install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(install); }else{ Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show(); } } } }

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

DownloadManager下载完成后会发出一个广播 android.intent.action.DOWNLOAD_COMPLETE 新建一个广播接收者即可:

清单配置:

先添加网络下载的权限:

<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

  • 1

  • 2

  • 1

  • 2

再添加静态广播:

<receiver android:name=".ApkInstallReceiver"> <intent-filter> <action android:name="android.intent.action.DOWNLOAD_COMPLETE" /> </intent-filter> </receiver>

  • 1

  • 2

  • 3

  • 4

  • 5

  • 1

  • 2

  • 3

  • 4

  • 5

使用HttpUrlConnection下载

这种情况下载的话我们就不需要考虑id的问题,因为是直接在项目中下载,所以我们就是一个网络下载的过程,并且使用ProgressDialog显示下载信息及进度更新就ok了。

public class AppInnerDownLoder { public final static String SD_FOLDER = Environment.getExternalStorageDirectory()+ "/VersionChecker/"; private static final String TAG = AppInnerDownLoder.class.getSimpleName(); /** * 从服务器中下载APK */ @SuppressWarnings("unused") public static void downLoadApk(final Context mContext,final String downURL,final String appName ) { final ProgressDialog pd; // 进度条对话框 pd = new ProgressDialog(mContext); pd.setCancelable(false);// 必须一直下载完,不可取消 pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pd.setMessage("正在下载安装包,请稍后"); pd.setTitle("版本升级"); pd.show(); new Thread() { @Override public void run() { try { File file = downloadFile(downURL,appName, pd); sleep(3000); installApk(mContext, file); // 结束掉进度条对话框 pd.dismiss(); } catch (Exception e) { pd.dismiss(); } } }.start(); } /** * 从服务器下载最新更新文件 * * @param path * 下载路径 * @param pd * 进度条 * @return * @throws Exception */ private static File downloadFile(String path,String appName ,ProgressDialog pd) throws Exception { // 如果相等的话表示当前的sdcard挂载在手机上并且是可用的 if (Environment.MEDIA_MOUNTED.equals(Environment .getExternalStorageState())) { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); // 获取到文件的大小 pd.setMax(conn.getContentLength()); InputStream is = conn.getInputStream(); String fileName = SD_FOLDER + appName+".apk"; File file = new File(fileName); // 目录不存在创建目录 if (!file.getParentFile().exists()) file.getParentFile().mkdirs(); FileOutputStream fos = new FileOutputStream(file); BufferedInputStream bis = new BufferedInputStream(is); byte[] buffer = new byte[1024]; int len; int total = 0; while ((len = bis.read(buffer)) != -1) { fos.write(buffer, 0, len); total += len; // 获取当前下载量 pd.setProgress(total); } fos.close(); bis.close(); is.close(); return file; } else { throw new IOException("未发现有SD卡"); } } /** * 安装apk */ private static void installApk(Context mContext, File file) { Uri fileUri = Uri.fromFile(file); Intent it = new Intent(); it.setAction(Intent.ACTION_VIEW); it.setDataAndType(fileUri, "application/vnd.android.package-archive"); it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// 防止打不开应用 mContext.startActivity(it); } /** * 获取应用程序版本(versionName) * * @return 当前应用的版本号 */ private static double getLocalVersion(Context context) { PackageManager manager = context.getPackageManager(); PackageInfo info = null; try { info = manager.getPackageInfo(context.getPackageName(), 0); } catch (NameNotFoundException e) { Log.e(TAG, "获取应用程序版本失败,原因:" + e.getMessage()); return 0.0; } return Double.valueOf(info.versionName); } /** * byte(字节)根据长度转成kb(千字节)和mb(兆字节) * * @param bytes * @return */ public static String bytes2kb(long bytes) { BigDecimal filesize = new BigDecimal(bytes); BigDecimal megabyte = new BigDecimal(1024 * 1024); float returnValue = filesize.divide(megabyte, 2, BigDecimal.ROUND_UP) .floatValue(); if (returnValue > 1) return (returnValue + "MB"); BigDecimal kilobyte = new BigDecimal(1024); returnValue = filesize.divide(kilobyte, 2, BigDecimal.ROUND_UP) .floatValue(); return (returnValue + "KB"); } }

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

  • 50

  • 51

  • 52

  • 53

  • 54

  • 55

  • 56

  • 57

  • 58

  • 59

  • 60

  • 61

  • 62

  • 63

  • 64

  • 65

  • 66

  • 67

  • 68

  • 69

  • 70

  • 71

  • 72

  • 73

  • 74

  • 75

  • 76

  • 77

  • 78

  • 79

  • 80

  • 81

  • 82

  • 83

  • 84

  • 85

  • 86

  • 87

  • 88

  • 89

  • 90

  • 91

  • 92

  • 93

  • 94

  • 95

  • 96

  • 97

  • 98

  • 99

  • 100

  • 101

  • 102

  • 103

  • 104

  • 105

  • 106

  • 107

  • 108

  • 109

  • 110

  • 111

  • 112

  • 113

  • 114

  • 115

  • 116

  • 117

  • 118

  • 119

  • 120

  • 121

  • 122

  • 123

  • 124

  • 125

  • 126

  • 127

  • 128

  • 129

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

  • 50

  • 51

  • 52

  • 53

  • 54

  • 55

  • 56

  • 57

  • 58

  • 59

  • 60

  • 61

  • 62

  • 63

  • 64

  • 65

  • 66

  • 67

  • 68

  • 69

  • 70

  • 71

  • 72

  • 73

  • 74

  • 75

  • 76

  • 77

  • 78

  • 79

  • 80

  • 81

  • 82

  • 83

  • 84

  • 85

  • 86

  • 87

  • 88

  • 89

  • 90

  • 91

  • 92

  • 93

  • 94

  • 95

  • 96

  • 97

  • 98

  • 99

  • 100

  • 101

  • 102

  • 103

  • 104

  • 105

  • 106

  • 107

  • 108

  • 109

  • 110

  • 111

  • 112

  • 113
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

由于文章篇幅原因,我只把面试题列了出来,详细的答案,我整理成了一份PDF文档,这份文档还包括了还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 ,帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 114

  • 115

  • 116

  • 117

  • 118

  • 119

  • 120

  • 121

  • 122

  • 123

  • 124

  • 125

  • 126

  • 127

  • 128

  • 129

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

  • 50

  • 51

  • 52

  • 53

  • 54

  • 55

  • 56

  • 57

  • 58

  • 59

  • 60

  • 61

  • 62

  • 63

  • 64

  • 65

  • 66

  • 67

  • 68

  • 69

  • 70

  • 71

  • 72

  • 73

  • 74

  • 75

  • 76

  • 77

  • 78

  • 79

  • 80

  • 81

  • 82

  • 83

  • 84

  • 85

  • 86

  • 87

  • 88

  • 89

  • 90

  • 91

  • 92

  • 93

  • 94

  • 95

  • 96

  • 97

  • 98

  • 99

  • 100

  • 101

  • 102

  • 103

  • 104

  • 105

  • 106

  • 107

  • 108

  • 109

  • 110

  • 111

  • 112

  • 113
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-62G48BrY-1713803441321)]

[外链图片转存中…(img-WBaO79c2-1713803441323)]

[外链图片转存中…(img-zx8QD3ru-1713803441324)]

[外链图片转存中…(img-WA6grj2o-1713803441325)]

[外链图片转存中…(img-C11IyKbQ-1713803441326)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

[外链图片转存中…(img-kFYiJoAx-1713803441327)]

最后

由于文章篇幅原因,我只把面试题列了出来,详细的答案,我整理成了一份PDF文档,这份文档还包括了还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 ,帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值