Android中Apk的下載與安裝

原文地址:http://www.reibang.com/p/a6cad97ea54f

相信很多應用都是采用內部下載的方式粥脚,這樣的體驗肯定比跳轉到瀏覽器好得多亭螟!而應用商店審核周期長秋茫,無法實時更新最新應用夯膀!所以內部下載更新就顯得尤為重要鲸匿!

1.要美觀好看逗抑,給用戶實時的反饋下載情況:

界面體現(xiàn)為下載百分比%,下載速度 kb/s夹攒,圓環(huán)進度

2.下載完成后要自動安裝:

Android6.0蜘醋,需要動態(tài)申請權限,讀取寫入咏尝。
Android7.0压语,需要通過fileprovider的方式創(chuàng)建Uri
Android8.0,需要申請【安裝未知來源應用權限】

針對第一個問題编检,我們采用自定義View來完成胎食,可定制化高,樣式想怎樣改怎樣改允懂。而第二個問題就需要我們隊權限的申請和對路徑創(chuàng)建方式的注意了厕怜。

先來一個效果圖:


下載安裝apk.gif

這個其實就是簡單的一個Dialog了,中間的獅子圖片是應用LOGO蕾总,下面的正在下載就是一個文字描述粥航,難點主要是中間的進度圓圈和圓圈點上的行星進度。

1.1新建一個Dialog彈出框:DownloadCircleDialog
public class DownloadCircleDialog extends Dialog {
    public DownloadCircleDialog(Context context) {
        super(context, R.style.Theme_Ios_Dialog);
    }
    DownloadCircleView circleView;
    TextView tvMsg;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.download_circle_dialog_layout);
        this.setCancelable(false);//設置點擊彈出框外部谤专,無法取消對話框
        circleView = findViewById(R.id.circle_view);
        tvMsg = findViewById(R.id.tv_msg);
    }
    public void setProgress(int progress) {
        circleView.setProgress(progress);
    }
    public void setMsg(String msg){
        tvMsg.setText(msg);
    }
}

在style.xml中寫入樣式:Theme.Ios.Dialog

<!-- IOSDialog -->
    <style name="Theme.Ios.Dialog" parent="@android:style/Theme.Dialog">
        <!-- Dialog的windowFrame框為無 -->
        <!-- <item name="android:windowFrame">@null</item> -->
        <!-- 邊框 -->
        <item name="android:windowIsFloating">true</item>
        <!-- 是否浮現(xiàn)在activity之上 -->
        <item name="android:windowIsTranslucent">true</item>
        <!-- 半透明 -->
        <item name="android:windowNoTitle">true</item>
        <!-- 設置dialog的背景 -->
        <item name="android:windowBackground">@android:color/transparent</item>
        <!-- 背景是否模糊顯示 -->
        <item name="android:backgroundDimEnabled">true</item>
        <!-- 模糊 -->
        <item name="android:textColorPrimaryInverse">@android:color/black</item>
    </style>

layout中的布局文件:download_circle_dialog_layout

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/layout_notice"
    android:background="@color/transparent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <com.qiqia.duosheng.custom.DownloadCircleView
        android:id="@+id/circle_view"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:layout_marginBottom="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv_logo"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_centerInParent="true"
        android:layout_marginBottom="24dp"
        android:src="@mipmap/logo"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/tv_msg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/iv_logo"
        android:layout_centerHorizontal="true"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:text="正在下載..."
        android:textSize="12sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/iv_logo" />
</android.support.constraint.ConstraintLayout>
1.2中間下載進度圓圈:DownloadCircleView
public class DownloadCircleView extends View {
    Paint mBgPaint;
    Paint mStepPaint;
    Paint mTxtCirclePaint;
    Paint mTxtPaint;
    int outsideRadius=DpPxUtils.dp2px(100);
    int progressWidth =DpPxUtils.dp2px(2);
    float progressTextSize  = DpPxUtils.dp2px(12);
    Context context;
    public DownloadCircleView(Context context) {
        super(context);
    }
    public DownloadCircleView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }
    public DownloadCircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width;
        int height;
        int size = MeasureSpec.getSize(widthMeasureSpec);
        int mode = MeasureSpec.getMode(widthMeasureSpec);

        if (mode == MeasureSpec.EXACTLY) {
            width = size;
        } else {
            width = (int) ((2 * outsideRadius) + progressWidth);
        }
        size = MeasureSpec.getSize(heightMeasureSpec);
        mode = MeasureSpec.getMode(heightMeasureSpec);
        if (mode == MeasureSpec.EXACTLY) {
            height = size;
        } else {
            height = (int) ((2 * outsideRadius) + progressWidth);
        }
        setMeasuredDimension(width, height);
    }

    private void init(Context context) {
        int progressColor = Color.parseColor("#FF5836");//進度球顏色
        this.context = context;
        //灰色背景圓環(huán)
        mBgPaint = new Paint();
        mBgPaint.setStrokeWidth(progressWidth);
        mBgPaint.setColor(Color.GRAY);
        this.mBgPaint.setAntiAlias(true);
        this.mBgPaint.setStyle(Paint.Style.STROKE); //繪制空心圓
        //進度圓環(huán)
        mStepPaint = new Paint();
        mStepPaint.setStrokeWidth(progressWidth);
        mStepPaint.setColor(progressColor);
        this.mStepPaint.setAntiAlias(true);
        this.mStepPaint.setStyle(Paint.Style.STROKE); //繪制空心圓
        //進度衛(wèi)星球
        mTxtCirclePaint = new Paint();
        mTxtCirclePaint.setColor(progressColor);
        this.mTxtCirclePaint.setAntiAlias(true);
        this.mTxtCirclePaint.setStyle(Paint.Style.FILL); //繪制實心圓
        //進度文字5%
        mTxtPaint = new Paint();
        mTxtPaint.setTextSize(progressTextSize);
        mTxtPaint.setColor(Color.WHITE);
        this.mTxtPaint.setAntiAlias(true);

    }
    float maxProgress=100f;
    float progress  =0f;

    public void setProgress(float progress) {
        this.progress = progress;
        postInvalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //灰色圓圈
        int circlePoint = getWidth() / 2;
        canvas.drawCircle(circlePoint, circlePoint, outsideRadius, mBgPaint); //畫出圓
        //進度
        RectF oval = new RectF();
        oval.left=circlePoint - outsideRadius;
        oval.top=circlePoint - outsideRadius;
        oval.right=circlePoint + outsideRadius;
        oval.bottom=circlePoint + outsideRadius;
        float range = 360 * (progress / maxProgress);
        canvas.drawArc(oval, -90,  range, false, mStepPaint);  //根據(jù)進度畫圓弧
        //軌道圓和文字
        double x1 = circlePoint + outsideRadius * Math.cos((range-90) * 3.14 / 180);
        double y1 = circlePoint + outsideRadius * Math.sin((range-90) * 3.14 / 180);
        canvas.drawCircle((float) x1, (float) y1, progressTextSize*1.3f, mTxtCirclePaint);
        String txt = (int) progress + "%";
        float strwid  = mTxtPaint.measureText(txt);//直接返回參數(shù)字符串所占用的寬度
        canvas.drawText(txt,(float) x1-strwid/2, (float) y1+progressTextSize/2-progressWidth/2,mTxtPaint);

    }
}

這樣躁锡,下載樣式基本就完成了午绳,每次通過方法setProgress和setMsg就可以去設置下載的進度和速度了置侍!

下面說說下載:采用 okhttp來下載apk文件,通過 ProgressManager來監(jiān)聽進度拦焚,通過 AndPermission簡化動態(tài)申請權限

2.1首先我們寫個下載工具類:DownloadUtils
import android.os.Handler;
import android.os.Looper;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import me.jessyan.progressmanager.ProgressListener;
import me.jessyan.progressmanager.ProgressManager;
import me.jessyan.progressmanager.body.ProgressInfo;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class DownloadUtils {
    private static DownloadUtils instance;
    private OkHttpClient okHttpClient;
    private Handler mHandler; //所有監(jiān)聽器在 Handler 中被執(zhí)行,所以可以保證所有監(jiān)聽器在主線程中被執(zhí)行

    public static DownloadUtils getInstance() {
        if (instance == null) instance = new DownloadUtils();
        return instance;
    }

    private DownloadUtils() {
        this.mHandler = new Handler(Looper.getMainLooper());
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        okHttpClient = ProgressManager.getInstance().with(builder).build();
    }

    public interface OnDownloadListener{
        /**
         * 下載成功
         */
        void onDownloadSuccess();

        /**
         * @param progress 下載進度
         */
        void onDownloading(ProgressInfo progress);

        /**
         * 下載失敗
         */
        void onDownloadFailed();
    }

    /**
     * @param url      下載連接
     * @param saveDir  儲存下載文件的SDCard目錄
     * @param listener 下載監(jiān)聽
     */
    public void download(final String url, final String saveDir, final String saveName, final OnDownloadListener listener) {
        Request request = new Request.Builder().url(url).build();


        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                // 下載失敗
                listener.onDownloadFailed();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                // Okhttp/Retofit 下載監(jiān)聽
                InputStream is = null;
                byte[] buf = new byte[2048];
                int len = 0;
                FileOutputStream fos = null;
                // 儲存下載文件的目錄
                try {
                    is = response.body().byteStream();
                    File file = new File(saveDir, saveName);
                    if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
                    fos = new FileOutputStream(file);
                    while ((len = is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                    }
                    fos.flush();
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            // 下載完成
                            listener.onDownloadSuccess();
                        }
                    });

                } catch (Exception e) {
                    Log.e("下載異常", e.getMessage());
                    listener.onDownloadFailed();
                } finally {
                    try {
                        if (is != null) is.close();
                    } catch (IOException e) {
                    }
                    try {
                        if (fos != null) fos.close();
                    } catch (IOException e) {
                    }
                }
            }
        });
        ProgressManager.getInstance().addResponseListener(url, new ProgressListener() {
            @Override
            public void onProgress(ProgressInfo progressInfo) {
                listener.onDownloading(progressInfo);
            }

            @Override
            public void onError(long l, Exception e) {
                listener.onDownloadFailed();
            }
        });
    }

}

上面通過ProgressManager.getInstance().with(builder).build();創(chuàng)建的okHttpClient也就相當于把ProgressManager加入到了OkHttp中蜡坊,這樣ProgressManager監(jiān)聽才會有效!

3.使用
  • a .首先我們需要加入安裝位置來源權限到AndroidManifest.xml中:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
  • b. 然后我們在需要下載apk的頁面中加入:
   DownloadCircleDialog  dialogProgress;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dialogProgress = new DownloadCircleDialog(this);
        showNewVersion();
    }
  //1.權限申請赎败,通過后開始下載
    private void showNewVersion() {
        AndPermission.with(this)
                .runtime()
                .permission(Permission.READ_EXTERNAL_STORAGE, Permission.WRITE_EXTERNAL_STORAGE)
                .onGranted(data -> {
                    L.e("以獲得權限" + data.toString());
                    new AlertDialog.Builder(this).setTitle("軟件更新").setMessage("發(fā)現(xiàn)新版本")
                            .setPositiveButton("確定", (dialog, which) -> {
                                String down_url = "https://qd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk";
                                downloadApk(MainActivity.this, down_url);
                            })
                            .setNegativeButton("取消",null).show();

                })
                .onDenied(data -> L.e("未獲得權限" + data.toString())).start();
    }
    //2.開始下載apk
 
    public void downloadApk(final Activity context, String down_url) {
        dialogProgress.show();
        DownloadUtils.getInstance().download(down_url, SdUtils.getDownloadPath(), "QQ.apk", new DownloadUtils.OnDownloadListener() {
            @Override
            public void onDownloadSuccess() {
                dialogProgress.dismiss();
                L.i("恭喜你下載成功秕衙,開始安裝!==" + SdUtils.getDownloadPath() + "QQ.apk");
                ToastUtil.showShort("恭喜你下載成功僵刮,開始安裝据忘!");
                String successDownloadApkPath = SdUtils.getDownloadPath() + "QQ.apk";
                installApkO(MainActivity.this, successDownloadApkPath);
            }
            @Override
            public void onDownloading(ProgressInfo progressInfo) {
                dialogProgress.setProgress(progressInfo.getPercent());
                boolean finish = progressInfo.isFinish();
                if (!finish) {
                    long speed = progressInfo.getSpeed();
                    dialogProgress.setMsg("(" + (speed > 0 ? FormatUtils.formatSize(context, speed) : speed) + "/s)正在下載...");
                } else {
                    dialogProgress.setMsg("下載完成!");
                }
            }
            @Override
            public void onDownloadFailed() {
                dialogProgress.dismiss();
                ToastUtil.showShort("下載失敻愀狻勇吊!");
            }
        });

    }
     // 3.下載成功,開始安裝,兼容8.0安裝位置來源的權限
    private void installApkO(Context context, String downloadApkPath) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //是否有安裝位置來源的權限
            boolean haveInstallPermission = getPackageManager().canRequestPackageInstalls();
            if (haveInstallPermission) {
                L.i("8.0手機已經(jīng)擁有安裝未知來源應用的權限窍仰,直接安裝汉规!");
                AppUtils.installApk(context, downloadApkPath);
            } else {
                new CakeResolveDialog(context, "安裝應用需要打開安裝未知來源應用權限,請去設置中開啟權限", new CakeResolveDialog.OnOkListener() {
                    @Override
                    public void onOkClick() {
                        Uri packageUri = Uri.parse("package:"+ AppUtils.getAppPackageName());
                        Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,packageUri);
                        startActivityForResult(intent,10086);
                    }
                }).show();
            }
        } else {
            AppUtils.installApk(context, downloadApkPath);
        }
    }
    //4.開啟了安裝未知來源應用權限后驹吮,再次進行步驟3的安裝针史。
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 10086) {
            L.i("設置了安裝未知應用后的回調晶伦。。啄枕。");
            String successDownloadApkPath = SdUtils.getDownloadPath() + "QQ.apk";
            installApkO(MainActivity.this, successDownloadApkPath);
        }
    }

上面代碼第一段showNewVersion就是對讀寫權限的申請婚陪,downloadApk下載進度的監(jiān)聽,下載完成通過installApkO來安裝频祝,installApkO中判斷如果沒有安裝位置來源的權限就跳轉到設置開啟安裝位置來源權限的頁面近忙,設置完成后回到這個頁面繼續(xù)安裝!
上面的AppUtils.installApk是我寫的一個工具方法智润,

public static  void installApk(Context context,String downloadApk) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        File file = new File(downloadApk);
        L.i("安裝路徑=="+downloadApk);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            Uri apkUri = FileProvider.getUriForFile(context, AppUtils.getAppPackageName()+".fileprovider", file);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        } else {
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            Uri uri = Uri.fromFile(file);
            intent.setDataAndType(uri, "application/vnd.android.package-archive");
        }
        context.startActivity(intent);

    }

判斷如果>=7.0就通過fileprovider來創(chuàng)建Uri,避免安裝出現(xiàn)解析包異常及舍!6.0的讀寫權限通過showNewVersion()方法進行了申明,8.0的安裝未知來源應用權限在installApkO進行了判斷申請窟绷,從而使安裝APK兼容了6锯玛,7,8兼蜈!9.0的機子還沒用過攘残,不過如果沒改動,應該也可以安裝为狸!

這樣一個apk從開始下載歼郭,進度顯示到安裝就完成了!說起來就是一個apk的下載安裝辐棒,但是其實代碼量和坑還是挺多的:

坑一:最開始沒有使用ProgressManager來進度監(jiān)聽病曾,而是在download方法的寫文件中監(jiān)聽下載進度:

public void download(final String url, final String saveDir, final OnDownloadListener listener) {
        Request request = new Request.Builder().url(url).build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                // 下載失敗
                listener.onDownloadFailed();
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                InputStream is = null;
                byte[] buf = new byte[2048];
                int len = 0;
                FileOutputStream fos = null;
                // 儲存下載文件的目錄
                String savePath = isExistDir(saveDir);
                try {
                    is = response.body().byteStream();
                    long total = response.body().contentLength();
                    File file = new File(savePath, getNameFromUrl(url));
                    fos = new FileOutputStream(file);
                    long sum = 0;
                    while ((len = is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                        sum += len;
                        int progress = (int) (sum * 1.0f / total * 100);
                        // 下載中
                        listener.onDownloading(progress);
                    }
                    fos.flush();
                    // 下載完成
                    listener.onDownloadSuccess();
                } catch (Exception e) {
                    listener.onDownloadFailed();
                } finally {
                    try {
                        if (is != null)
                            is.close();
                    } catch (IOException e) {
                    }
                    try {
                        if (fos != null)
                            fos.close();
                    } catch (IOException e) {
                    }
                }
            }
        });
    }

這樣監(jiān)聽到的下載進度并不是真的下載進度,而是下載文件后寫入到手機的速度漾根,體現(xiàn)到界面就是最開始下載進度是0%一直不動泰涂,然后2秒鐘就從0%轉圈到100%,就下載完成了辐怕,給用戶感覺一點都不真實逼蒙!

坑二:下載完成后解析包錯誤:

a.主要原因就是沒有使用Uri apkUri = FileProvider.getUriForFile(context, AppUtils.getAppPackageName()+".fileprovider", file);的方式來創(chuàng)建Uri 安裝,
b.還有就是因為文件名字不正確寄疏,最開始我的download方法中沒有saveName方法是牢,而是通過下載地址截取最后的“/”來寫入文件名的,但是有的下載地址并不是以apk結尾陕截,從而導致解析包錯誤驳棱!
c.還有就是根本沒有這個文件路徑,從而導致寫錯誤艘策,所以在download方法中寫入本地文件前我加入了如果沒有文件路徑就先創(chuàng)建當前路徑

File file = new File(saveDir, saveName);
if (!file.getParentFile().exists()) file.getParentFile().mkdirs();

坑三:下載出錯:CertPathValidatorException: Trust anchor for certification path not found蹈胡,(上面的代碼沒有加入,因為每個人的OkHttpClient都不同,我是寫了個工具類來創(chuàng)建的OkHttpClient罚渐,所以工具類中加入了進度讀取和跳過SSL驗證的却汉,由于自私原因,大家自己加吧荷并。)
相信有的應用是放在自己的服務器的合砂,而又有https,但很多都是沒有證書的源织,導致下載不了翩伪!所以我們就需要Okhttp繞過證書驗證,參考:
https://blog.csdn.net/O0mm0O/article/details/76686917
坑四:無法安裝:
Android8.0需要安裝權限:<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>,同時7.0以上還需要安裝未知來源的權限谈息,不然也無法安裝缘屹,可以參考:
http://www.reibang.com/p/a6209440a518
坑五:無法安裝:由于配置xml中的provider_paths.xml只寫了

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

導致無法讀取路徑而無法安裝,因為我們是下載到Download的所以還要加入:

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

這樣才算完整侠仇!關于fileprovider路徑介紹:
https://blog.csdn.net/leilifengxingmw/article/details/57405908
坑六:安裝完成后閃退轻姿,或安裝完成后點擊打開閃退
在大部分手機上沒有問題,但在Vivo X9上居然安裝完成后閃退了逻炊,我覺得這應該是應用已經(jīng)死了互亮,所有安裝完成后立即啟動有問題,而其他手機就沒有問題余素,我覺得還是Vivo手機的廠商定制問題豹休!所以解決辦法就是安裝的時候啟動一個新的任務棧來安裝:

 public static  void installApk(Context context,String downloadApk) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        File file = new File(downloadApk);
        L.i("安裝路徑=="+downloadApk);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            Uri apkUri = FileProvider.getUriForFile(context, AppUtils.getAppPackageName()+".fileprovider", file);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        } else {
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            Uri uri = Uri.fromFile(file);
            intent.setDataAndType(uri, "application/vnd.android.package-archive");
        }
        context.startActivity(intent);

    }

其中:intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);就是關鍵啦!

下個應用如果還需要下載桨吊,我就把上面的代碼復制進去威根,免得每次都要去找寫了下載安裝APK代碼的項目,然后一部分一部分去查找復制屏积!這么多代碼記肯定是記不住的医窿,這輩子都不可能記住的,所以寫這里方便下次Copy!

PS:由于一些同學要工具炊林,我又不可能要一個工具發(fā)一個。所以我把我收集的工具類發(fā)出來卷要,需要什么工具可以自行篩選:SmallUtils

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末渣聚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子僧叉,更是在濱河造成了極大的恐慌奕枝,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓶堕,死亡現(xiàn)場離奇詭異隘道,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門谭梗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來忘晤,“玉大人,你說我怎么就攤上這事激捏∩杷” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵远舅,是天一觀的道長闰蛔。 經(jīng)常有香客問我,道長图柏,這世上最難降的妖魔是什么序六? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮蚤吹,結果婚禮上难咕,老公的妹妹穿的比我還像新娘。我一直安慰自己距辆,他們只是感情好余佃,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著跨算,像睡著了一般爆土。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上诸蚕,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天步势,我揣著相機與錄音,去河邊找鬼背犯。 笑死坏瘩,一個胖子當著我的面吹牛,可吹牛的內容都是我干的漠魏。 我是一名探鬼主播倔矾,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼柱锹!你這毒婦竟也來了哪自?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤禁熏,失蹤者是張志新(化名)和其女友劉穎壤巷,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瞧毙,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡胧华,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年寄症,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矩动。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡有巧,死狀恐怖,靈堂內的尸體忽然破棺而出铅忿,到底是詐尸還是另有隱情剪决,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布檀训,位于F島的核電站柑潦,受9級特大地震影響,放射性物質發(fā)生泄漏峻凫。R本人自食惡果不足惜渗鬼,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望荧琼。 院中可真熱鬧譬胎,春花似錦、人聲如沸命锄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脐恩。三九已至镐侯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驶冒,已是汗流浹背苟翻。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留骗污,地道東北人崇猫。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像需忿,于是被迫代替她去往敵國和親诅炉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359