app版本更新和下載,通知欄實時進度(使用服務和廣播)

每個app都需要有版本更新的功能,下面簡單介紹一下最近在項目中使用的app更新功能。
1、首先需要使用服務和廣播實現後臺更新,使用到了xUtils,其他的網絡請求框架代碼類似。
服務代碼如下,註釋寫的很詳細

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;

import org.xutils.common.Callback;
import org.xutils.ex.HttpException;
import org.xutils.http.RequestParams;
import org.xutils.x;

import java.io.File;
import java.text.NumberFormat;

/**
 * 下載的services,配合xutils的httputils使用,完成notification的下載功能
 */
public class UpdateService extends Service {

    //是否已經開始下載
    private boolean isBegin = false;
    Intent intent;
    private NumberFormat numberFormat;
    public static Callback.Cancelable downLoadHandler;


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {

        return null;
    }


    @Override
    public void onCreate() {
        super.onCreate();
        intent = new Intent();
        numberFormat = NumberFormat.getInstance();
        numberFormat.setMaximumFractionDigits(2);
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        String downUrl = intent.getStringExtra("downUrl");
        String filePath = intent.getStringExtra("filePath");
        //如果下載地址爲空,則什麼都不幹
        if (TextUtils.isEmpty(downUrl)) {
            stopSelf();
//            throw  new IllegalArgumentException("the download url is empty!!!!");
            return START_NOT_STICKY;
        }
        if (isBegin) {
            //此時已經開始了
            return START_NOT_STICKY;
        } else {
            isBegin = true;
        }
        downLoad(downUrl,filePath);
        return super.onStartCommand(intent, flags, startId);
    }

    int progress = -1;
    private void downLoad(final String downUrl,String filePath) {

        RequestParams requestParams = new RequestParams(downUrl);
        requestParams.setSaveFilePath(filePath);
        downLoadHandler = x.http().get(requestParams, new Callback.ProgressCallback<File>() {

            @Override
            public void onSuccess(File result) {
                intent.setAction("com.ycb.www.complete");
                intent.putExtra("filepath", result.getAbsolutePath());
                Log.i("tag", "onSuccess");
                Log.i("tag", result.getAbsolutePath());
                sendBroadcast(intent);
                stopSelf();
            }

            @Override
            public void onError(Throwable ex, boolean isOnCallback) {
                intent.setAction("com.ycb.www.failed");
                intent.putExtra("downUrl", downUrl);
                Log.i("tag", "onFailure!!!"+ex.getMessage());
//                sendBroadcast(intent);

                if (ex instanceof HttpException) {
                    HttpException httpEx = (HttpException) ex;
                    Log.i("tag","onError:"+httpEx.getCode()+httpEx.getMessage());
                }
            }

            @Override
            public void onCancelled(CancelledException cex) {
                Log.i("tag", "onCancelled");
                stopSelf();
            }

            @Override
            public void onFinished() {
                Log.i("tag", "onFinished");
//                 stopSelf();
            }

            @Override
            public void onWaiting() {
                Log.i("tag", "onWaiting");

            }

            @Override
            public void onStarted() {
                Log.i("tag","Started");
                intent.putExtra("rate",0);
                intent.setAction("com.ycb.www.updating");
                sendBroadcast(intent);
            }

            @Override
            public void onLoading(long total, long current, boolean isDownloading) {
                Double rate= (double)current / (double)total;
                String format = numberFormat.format(rate);
                int   r= (int) (Double.valueOf(format)*100);
                Log.i("tag", ""+r);
                intent.putExtra("rate", r);
                intent.setAction("com.ycb.www.updating");
                sendBroadcast(intent);

            }
        });


    }


}

2、然後添加一個廣播,用於實時處理下載進度,並在通知欄實時顯示

import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.widget.RemoteViews;

import java.io.File;

/**
 * notification更新的廣播接收者,根據action不同,做出的結果不同,
 * 其中intent因爲是同一個intent的,所以並沒有new 新的
 */
public class UpdateReceiver extends BroadcastReceiver {


    private NotificationManager manager;
    private RemoteViews views;
    private Notification notification;


    @Override
    public void onReceive(Context context, Intent intent) {
        if (notification == null) {

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
                initNotification(context);
            else {
                initNotificationForLowVersion(context);
            }
        }


        String action = intent.getAction();
        switch (action) {
            case "com.ycb.www.cancel":
                manager.cancel(0);
                UpdateService.downLoadHandler.cancel();
                break;
            case "com.ycb.www.failed":
                intent.setAction("com.ycb.www.restart");
                PendingIntent failedpendingIntent = PendingIntent.getBroadcast(context, 200, intent, PendingIntent.FLAG_CANCEL_CURRENT);
                views.setOnClickPendingIntent(R.id.ll_content, failedpendingIntent);
                views.setTextViewText(R.id.tv_info, "下載失敗,點擊重試");
                manager.notify(0, notification);
                break;
            case "com.ycb.www.restart":
                manager.cancel(0);
                intent.setClass(context, UpdateService.class);
                context.startService(intent);
                break;
            case "com.ycb.www.install":
                manager.cancel(0);
                Intent startInstall = new Intent();
                startInstall.setAction(Intent.ACTION_VIEW);
                String filepath = intent.getStringExtra("filepath");
                startInstall.setDataAndType(Uri.fromFile(new File(filepath)), "application/vnd.android.package-archive");
                startInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(startInstall);
                break;
            case "com.ycb.www.complete":
                intent.setAction("com.ycb.www.install");
                PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 200, intent, PendingIntent.FLAG_CANCEL_CURRENT);
                views.setOnClickPendingIntent(R.id.ll_content, pendingIntent);
                views.setTextViewText(R.id.tv_info, "下載完成,點擊安裝");
                views.setProgressBar(R.id.progressBar, 100, 100, false);
                manager.notify(0, notification);
                break;
            case "com.ycb.www.updating":
                int rate = intent.getIntExtra("rate", 0);
                views.setTextViewText(R.id.tv_info, "正在下載...." + rate + "%");
                views.setProgressBar(R.id.progressBar, 100, rate, false);
                manager.notify(0, notification);
        }

    }

    private void initNotificationForLowVersion(Context context) {
        //設置notifiction佈局
        views = new RemoteViews(context.getPackageName(), R.layout.notification_update);

        notification = new Notification();

        notification.when = System.currentTimeMillis();

        notification.tickerText = "xxxx新版正在下載";
        //設置view
        notification.contentView = views;
        //設置小圖標
        notification.icon = R.mipmap.icon;
        //設置佈局文件中的textView的內容
        views.setTextViewText(R.id.tv_info, "下載中....0%");

        //設置佈局文件中的ProgressBar進度
        views.setProgressBar(R.id.progressBar, 100, 0, false);
        //退出的intent
        Intent intent = new Intent("com.ycb.www.cancel");
        //退出的延遲意圖
        PendingIntent mPendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), 200, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        //點擊之後退出
        views.setOnClickPendingIntent(R.id.ib_close, mPendingIntent);
    }


    /**
     * 初始化notification
     *
     * @param context
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void initNotification(Context context) {
        manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        views = new RemoteViews(context.getPackageName(), R.layout.notification_update);
        Notification.Builder builder = new Notification.Builder(context.getApplicationContext());
        notification = builder.setAutoCancel(false).setSmallIcon(R.mipmap.icon).setContentText("下載中").setContentTitle("下載").
                setWhen(System.currentTimeMillis()).setTicker("xxxxx新版正在下載")
                .setContent(views).build();

        views.setTextViewText(R.id.tv_info, "下載中....0%");
        views.setProgressBar(R.id.progressBar, 100, 0, false);
        Intent intent = new Intent("com.ycb.www.cancel");
        PendingIntent mPendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), 200, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        views.setOnClickPendingIntent(R.id.ib_close, mPendingIntent);

    }

}

3、在MainActivity或者LoginActivity頁面檢測app版本是否有更新,若有更新,開啓服務,註冊廣播,開始下載更新操作
在onCreate方法中添加如下代碼:

//檢查版本更新
    private void checkAppVersion() {

        try {
            PackageManager manager = this.getPackageManager();
            PackageInfo info = manager.getPackageInfo(this.getPackageName(), 0);
            versionCode = info.versionName;
            System.out.println("versionCode:" + versionCode);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                OkHttpUtils.get()
                        .url(Constant.DB_URL + Constant.PORT + Constant.XHB_DIR + Constant.GET_APP_VERSION_DATA)
                        .addParams("type", "0")
                        .build()
                        .execute(new StringCallback() {
                            @Override
                            public void onError(Call call, Exception e) {
                                Toast.makeText(LoginActivity.this, "網絡異常", Toast.LENGTH_SHORT).show();
                            }

                            @Override
                            public void onResponse(String response) {
                                System.out.println("downApp:" + response);
                                //版本號對比,若不同下載app
                                downLoadApp(response);
                            }
                        });
            }
        }).start();

    }

倘若有更新時,開啓服務執行下載操作,執行下面方法:

//比對版本號,更新app
    private void downLoadApp(String response) {
        final AppVersionResponse appVersionResponse = new Gson().fromJson(response, AppVersionResponse.class);
        AppVersionResponse.DataBean dataBean = appVersionResponse.getData();
        if (appVersionResponse.isIsSuccess() && dataBean != null) {
            if (!versionCode.equals(appVersionResponse.getData().getCode())) {
                //強制更新
                if(dataBean.getIsForced() == 1){
                    btn_land.setEnabled(false);
                    //彈出窗口是否強制更新版本
                    AlertDialog alertDialog = new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_info).setTitle("版本升級")
                            .setMessage(dataBean.getContent())
                            .setCancelable(false)
                            .setPositiveButton("確定", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialogInterface, int i) {
                                    //下載操作
                                    downLoad(appVersionResponse.getData().getCode());
                                }
                            }).create();
                    alertDialog.show();
                }else if(dataBean.getIsForced() == 0){
                    //非強制更新
//                    btn_land.setEnabled(false);
                    //彈出窗口是否強制更新版本
                    AlertDialog alertDialog = new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_info).setTitle("版本升級提示")
                            .setMessage(dataBean.getContent())
                            .setCancelable(false)
                            .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialogInterface, int i) {
                                    dialogInterface.dismiss();
                                }
                            })
                            .setPositiveButton("確定", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialogInterface, int i) {
                                    //下載操作
                                    downLoad(appVersionResponse.getData().getCode());
                                }
                            }).create();
                    alertDialog.show();
                }

            }
        }
    }
    //下載app
    private void downLoad(final String version) {
        updateReceiver = new UpdateReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.ycb.www.cancel");
        filter.addAction("com.ycb.www.failed");
        filter.addAction("com.ycb.www.restart");
        filter.addAction("com.ycb.www.install");
        filter.addAction("com.ycb.www.complete");
        filter.addAction("com.ycb.www.updating");
        registerReceiver(updateReceiver,filter);

        Intent intent = new Intent(this, UpdateService.class);
        String url = Constant.DB_URL + Constant.PORT + Constant.XHB_DIR + Constant.DOWNLOAD_APP_URL;
        intent.putExtra("downUrl", url);
        intent.putExtra("filePath",Environment.getExternalStorageDirectory().getPath()+"/Downloads/xiahubao_"+version+".apk");
        startService(intent);

    }

另外需要在配置文件中添加下面的服務和廣播註冊

 <receiver android:name=".appUpdateUtils.UpdateReceiver">
            <intent-filter>
                <action android:name="com.ycb.www.complete" />
                <action android:name="com.ycb.www.install" />
                <action android:name="com.ycb.www.cancel" />
                <action android:name="com.ycb.www.updating" />
                <action android:name="com.ycb.www.failed" />
                <action android:name="com.ycb.www.restart" />
            </intent-filter>
        </receiver>

 <service android:name=".appUpdateUtils.UpdateService" />

最後,在通知欄顯示的自定義進度條代碼如下,緊供參考

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_content"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/img_menu"
        android:layout_width="40dp"
        android:layout_height="40dp"

        android:src="@mipmap/icon" />

    <LinearLayout
        android:layout_width="0dip"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:layout_marginLeft="10dp"
        android:layout_marginTop="6dp"
        android:orientation="vertical">

        <ProgressBar
            android:id="@+id/progressBar"

            style="?android:attr/progressBarStyleHorizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:progressDrawable="@drawable/seek_bar_progress_bg" />

        <TextView
            android:id="@+id/tv_info"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="6dp"
            android:textColor="@color/textBlackColor"

            />
    </LinearLayout>

    <ImageButton
        android:id="@+id/ib_close"
        android:layout_width="15dp"
        android:layout_height="15dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="3dp"
        android:background="@null"
        android:src="@android:drawable/ic_menu_close_clear_cancel" />


</LinearLayout>

progressDrawable的配置如下,顯示的更美觀

<?xml version="1.0" encoding="utf-8"?>
<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background">
        <shape>
            <solid android:color="#c6c6c6" />
            <corners android:radius="3dp"/>
        </shape>
    </item>
    <item android:id="@android:id/secondaryProgress">
        <clip>
            <shape>
                <solid android:color="#c6c6c6" />
                <corners android:radius="3dp"/>
            </shape>
        </clip>
    </item>
    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <solid android:color="#06a7fa" />
                <corners android:radius="3dp"/>
            </shape>
        </clip>
    </item>
</layer-list>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章