1. 程式人生 > >安卓 app 本地升級下載後自動安裝(小米手機安裝包解析失敗問題)

安卓 app 本地升級下載後自動安裝(小米手機安裝包解析失敗問題)

 強制升級:

1.新建服務

package com.jy.mango.project.service;
import android.app.DownloadManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import 
android.database.Cursor; import android.net.Uri; import android.os.Binder; import android.os.Environment; import android.os.IBinder; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import android.util.LongSparseArray;
import android.webkit.MimeTypeMap; import com.jy.mango.project.application.installutils.IOUtils; import com.jy.mango.project.application.installutils.InstallUtil; import com.jy.mango.project.application.installutils.SystemManager; import com.jy.mango.project.utils.T; import java.io.File; import java.io.IOException;
import java.net.URI; import java.net.URISyntaxException; /** * Created by mango on 2017/11/6. */ public class UpdateService extends Service { private DownloadManager mDownloadManager; private DownloadBinder mBinder = new DownloadBinder(); private LongSparseArray<String> mApkPaths; private DownloadFinishReceiver mReceiver; @Override public void onCreate() { super.onCreate(); mDownloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); mApkPaths = new LongSparseArray<>(); //註冊下載完成的廣播 mReceiver = new DownloadFinishReceiver(); registerReceiver(mReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); } @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public void onDestroy() { unregisterReceiver(mReceiver);//取消註冊廣播接收者 super.onDestroy(); } public class DownloadBinder extends Binder { /** * 下載 * @param apkUrl 下載的url */ public long startDownload(String apkUrl){ //點選下載 //刪除原有的APK IOUtils.clearApk(UpdateService.this,"app.apk"); //使用DownLoadManager來下載 DownloadManager.Request request = new DownloadManager.Request(Uri.parse(apkUrl)); //將檔案下載到自己的Download資料夾下,必須是External//這是DownloadManager的限制 File file = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "app.apk"); request.setDestinationUri(Uri.fromFile(file)); //新增請求 開始下載 long downloadId = mDownloadManager.enqueue(request); Log.d("DownloadBinder", file.getAbsolutePath()); mApkPaths.put(downloadId,file.getAbsolutePath()); return downloadId; } /** * 獲取進度資訊 * @param downloadId 要獲取下載的id * @return 進度資訊 max-100 */ public int getProgress(long downloadId) { //查詢進度 DownloadManager.Query query = new DownloadManager.Query() .setFilterById(downloadId); Cursor cursor = null; int progress = 0; try { cursor = mDownloadManager.query(query);//獲得遊標 if (cursor != null && cursor.moveToFirst()) { //當前的下載量 int downloadSoFar = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); //檔案總大小 int totalBytes = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); progress = (int) (downloadSoFar * 1.0f / totalBytes * 100); } } finally { if (cursor != null) { cursor.close(); } } return progress; } } //下載完成的廣播 private class DownloadFinishReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { //下載完成的廣播接收者 long completeDownloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); String apkPath = mApkPaths.get(completeDownloadId); Log.d("DownloadFinishReceiver", apkPath); if (!apkPath.isEmpty()){ SystemManager.setPermission(apkPath);//提升讀寫許可權,否則可能出現解析異常 InstallUtil.install(context,apkPath); }else { Log.e("DownloadFinishReceiver", "apkPath is null"); } } } }

2.配置manifest檔案

<service android:name=".UpdateService"/>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="包名"
android:grantUriPermissions="true"
android:exported="false">
<!--元資料-->
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_path" />
</provider>

3.指定path

在res下新建資料夾xml  新建xml檔案命名為path

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
    <paths>
        <external-path path="" name="download"/>
    </paths>
</resources>

4. 啟動服務下載檔案

private UpdateService.DownloadBinder mDownloadBinder;
private ServiceConnection mConnection = new ServiceConnection() {
    @Override
public void onServiceConnected(ComponentName name, IBinder service) {
        mDownloadBinder = (UpdateService.DownloadBinder) service;
}

    @Override
public void onServiceDisconnected(ComponentName name) {
        mDownloadBinder = null;
}
};
啟動服務
Intent intent = new Intent(this, UpdateService.class);
startService(intent);
bindService(intent, mConnection, BIND_AUTO_CREATE);//繫結服務

開始下載
if (mDownloadBinder != null) {
    long downloadId = mDownloadBinder.startDownload(serviceUrl);
startCheckProgress(downloadId);
dialog.dismiss();
}
監聽下載
//開始監聽進度
private void startCheckProgress(long downloadId) {
    Observable
            .interval(100, 200, TimeUnit.MILLISECONDS, Schedulers.io())//無限輪詢,準備查詢進度,io執行緒執行
.filter(times -> mDownloadBinder != null)
            .map(i -> mDownloadBinder.getProgress(downloadId))//獲得下載進度
.takeUntil(progress -> progress >= 100)//返回true就停止了,當進度>=100就是下載完成了
.distinct()//去重複
.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new ProgressObserver());
}



//觀察者
private class ProgressObserver implements Observer<Integer> {

    @Override
public void onSubscribe(Disposable d) {

    }

    @Override
public void onNext(Integer progress){//設定進度
}

    @Override
public void onError(Throwable throwable) {
        throwable.printStackTrace();
Toast.makeText(MainActivity.this, "出錯", Toast.LENGTH_SHORT).show();
}

    @Override
public void onComplete() {
        Toast.makeText(MainActivity.this, "下載完成", Toast.LENGTH_SHORT).show();
}
}


5. 修改檔案許可權,並且在下載完成後刪除檔案,關閉檔案流

 class SystemManager {

    /**
     * 應用程式執行命令獲取 Root許可權,裝置必須已破解(獲得ROOT許可權)
     *
     * @param command 命令:String apkRoot="chmod 777 "+getPackageCodePath();
     * @return  0 命令執行成功
*/
public static int RootCommand(String command) {
        Process process = null;
DataOutputStream os = null;
        try {
            process = Runtime.getRuntime().exec("su");
os = new DataOutputStream(process.getOutputStream());
os.writeBytes(command + "\n");
os.writeBytes("exit\n");
os.flush();
            int i = process.waitFor();
Log.d("SystemManager", "i:" + i);
            return i;
} catch (Exception e) {
            Log.d("SystemManager", e.getMessage());
            return -1;
} finally {
            try {
                if (os != null) {
                    os.close();
}
                process.destroy();
} catch (Exception e) {
            }
        }
    }

    /**
     * 提升讀寫許可權
* @param filePath 檔案路徑
* @return
* @throws IOException
     */
public static void setPermission(String filePath)  {
        String command = "chmod " + "777" + " " + filePath;
Runtime runtime = Runtime.getRuntime();
        try {
            runtime.exec(command);
} catch (IOException e) {
            e.printStackTrace();
}
    }

關閉檔案流及成功後刪除APK檔案

IOUtils {
    public static void closeIO(Closeable... closeables) {
        if (closeables != null) {
            for (Closeable closeable : closeables) {
                if (closeable != null) {
                    try {
                        closeable.close();
} catch (IOException e) {
                        e.printStackTrace();
}
                }
            }
        }
    }

    /**
     * 刪除之前的apk
     *
     * @param apkName apk名字
* @return
*/
public static File clearApk(Context context, String apkName) {
        File apkFile = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), apkName);
        if (apkFile.exists()) {
            apkFile.delete();
}
        return apkFile;
}
}

6.

OK啦!判斷許可權,並請求許可權,6.0儲存許可權要申請,解決拒絕後的閃退事件。

許可權判斷
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
{

    if (requestCode == MY_PERMISSIONS_REQUEST_CALL_PHONE) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED){
            if (mDownloadBinder != null) {
                long downloadId = mDownloadBinder.startDownload(serviceUrl);
startCheckProgress(downloadId);
}
        } else{
            T.showLong("該應用已被禁止儲存許可權" +
                    "\n請在設定>許可權管理中授權");
alertDialog.show();
}
        return;
}
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}