IntentService+Notifcation实现应用app后台下载完成后并安装(已适配8.0)

更新———————————-
现在已兼容8.0的通知栏显示,确保你的targetSdkVersion 是26或以上
以下为8.0的显示图片:
这里写图片描述
这里写图片描述

app的更新模块放在后台服务可以大大提高app的体验,采用IntentService这种google为我们封装好的用于执行服务中有网络操作的类并搭配Notification来实现一下(下载工具用的是自带的URLConnection,因为Retrofit+rxjava并没有提供进度的回调,网上也有很多改进的方案,可以自定义让retrofit+rxjava实现进度的回调,在此,没有采用)

效果图如下:(看了一下淘宝的更新,所以就成这样的界面了,)
这里写图片描述
代码:自定义的IntentService类

/**
 * 更新包下载安装服务
 */
public class UpdateService extends IntentService {
    private static final int NOTIFY_DOWNLOAD= 0;
    private static final int NOTIFY_FINISH = 1;
    private static final String PENDING_INSTALL_ACTION = "gaoxin.com.inforindustry.click.toinstall";
    private Context mContext;
    private String apkUrl;
    public UpdateService() {
        super("UpdateService");
    }
    private NotificationUtils notificationUtils;
    private File  downapkfile;
    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what){
                case 0:
                    //正在下载中

                    RemoteViews contentView = notificationUtils.getNotification().contentView;
                    contentView.setTextViewText(R.id.notify_tv, "更新包下载中...");
                    contentView.setProgressBar(R.id.notify_progress_pb, 100, msg.arg1, false);
                    contentView.setTextViewText(R.id.notify_progress_tv,msg.arg1+"%");
                    // 更新UI
                    notificationUtils.getManager().notify(NOTIFY_DOWNLOAD,notificationUtils.getNotification());
                    break;
                case 1:
                    notificationUtils.cancelNotification(NOTIFY_DOWNLOAD);
                    createNotification(NOTIFY_FINISH);
                    break;
            }
            return true;
        }
    });
    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            apkUrl = intent.getStringExtra("apkurl");
            handleActionFoo(apkUrl);
        }
    }

    private void handleActionFoo(String param1) {
        if(NetworkUtils.isConnected()){
            createNotification(NOTIFY_DOWNLOAD);
            try {
                DownApk(param1);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    //发送消息进行更新进度条
    public void sendMessage(int what,int mprogress) {
        Message msg0 = mHandler.obtainMessage();
        msg0.what = what;
        msg0.arg1 = mprogress;
        mHandler.sendMessage(msg0);
    }
    private void DownApk(String param1) {
        int oldProcess = 0;
        if (SDCardUtils.isSDCardEnable()){
            downapkfile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/industry.apk");
            try {
                URL url = new URL(param1.trim());
                HttpURLConnection connection  = (HttpURLConnection) url.openConnection();
                if (connection.getResponseCode() == 200){
                    InputStream inputStream = connection.getInputStream();
                    FileOutputStream fos = new FileOutputStream(downapkfile);
                    //总长度
                    int totalLength = connection.getContentLength();

                    //已下载的长度
                    int currentLength  = 0;
                    byte[] bytes = new byte[512];
                    connection.connect();
                    int flag = 0;
                    while (flag < 100){

                        if (inputStream != null){
                            int read = inputStream.read(bytes);
                            if (read <= 0){
                                sendMessage(1,0);
                                break;
                            }else {
                                fos.write(bytes,0,read);
                                currentLength += read;
                                int mprogress = (int) ((currentLength*100)/totalLength);
                                if(oldProcess <= mprogress-5 ){
                                    // 避免notifymanager ANR,每下载百分之5才进行通知一次
                                    oldProcess =mprogress;
                                    sendMessage(0, mprogress);
                                }
                            }
                        }
                    }
                    fos.close();
                    inputStream.close();
                }
                connection.disconnect();

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }


    }

    private void createNotification(int notifyId) {
        switch (notifyId){
            case NOTIFY_DOWNLOAD:
                RemoteViews remoteViews = new RemoteViews(AppUtils.getAppPackageName(),R.layout.notify_custom_view_layout);
                remoteViews.setTextViewText(R.id.notify_tv,"正在下载...");
                remoteViews.setProgressBar(R.id.notify_progress_pb,100,0,false);
                remoteViews.setTextViewText(R.id.notify_progress_tv,"0%");
                notificationUtils.sendNotification(notifyId,"","",remoteViews,null);
                break;
            case NOTIFY_FINISH:
                Intent intent = new Intent(getApplicationContext(),NotificationBroadCast.class);
                intent.setAction(PENDING_INSTALL_ACTION);
                intent.putExtra("notifyId",NOTIFY_FINISH);
                PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,0,intent,PendingIntent.FLAG_ONE_SHOT);
                notificationUtils.sendNotification(notifyId,"点击安装","更新包已下载完成",null,pendingIntent);
                break;
            default:
                break;
        }

    }

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;
        //初始化通知窗口管理
        notificationUtils = new NotificationUtils(getApplicationContext());
    }
}

NotificationUtils,用来进行适配8.0系统

public class NotificationUtils extends ContextWrapper {

    private NotificationManager mManager;
    public static final String ANDROID_CHANNEL_ID = "com.gaoxin.industry.ANDROID";
    public static final String ANDROID_CHANNEL_NAME = "ANDROID CHANNEL";
    private Notification notification;
    public NotificationUtils(Context base) {
        super(base);
        if (Build.VERSION.SDK_INT >= 26){
            createChannels();
        }

    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    public void createChannels() {

        // create android channel
        NotificationChannel androidChannel = new NotificationChannel(ANDROID_CHANNEL_ID,
                ANDROID_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
        // Sets whether notifications posted to this channel should display notification lights
        androidChannel.enableLights(true);
        // Sets whether notification posted to this channel should vibrate.
        androidChannel.enableVibration(true);
        // Sets the notification light color for notifications posted to this channel
        androidChannel.setLightColor(Color.GREEN);
        // Sets whether notifications posted to this channel appear on the lockscreen or not
        androidChannel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);

        getManager().createNotificationChannel(androidChannel);
    }

    public NotificationManager getManager() {
        if (mManager == null) {
            mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        }
        return mManager;
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    public Notification.Builder getAndroidChannelNotification(String title,String content) {
        return new Notification.Builder(getApplicationContext(), ANDROID_CHANNEL_ID)
                .setContentTitle(title)
                .setContentText(content)
                .setSmallIcon(android.R.drawable.stat_notify_more)
                ;
    }
    public NotificationCompat.Builder getNotification_25(String title, String content){
        return new NotificationCompat.Builder(getApplicationContext())
                .setContentTitle(title)
                .setContentText(content)
                .setSmallIcon(android.R.drawable.stat_notify_more)
                ;
    }
    public void sendNotification(int id,String title, String content, RemoteViews remoteViews, PendingIntent intent){
        if (Build.VERSION.SDK_INT>=26){

                notification = getAndroidChannelNotification(title, content).setCustomContentView(remoteViews).setContentIntent(intent)
                        .build();
            getManager().notify(id,notification);
        }else{
                notification = getNotification_25(title, content).setCustomContentView(remoteViews).setContentIntent(intent).build();
            getManager().notify(id,notification);
        }
    }
    public void cancelNotification(int id){
        getManager().cancel(id);
    }
    public Notification getNotification(){
        if (notification != null){
            return notification;
        }
        return null;
    }
}

需要注意的点: Notification必须设置smallIcon这个属性,否则会报错,
如果是下载完成后不希望自动安装,而是点击后进行安装,可以使用PendingIntent这个类来进行触发,否则点击后是没有什么效果的,必须传递意图Intent

Android 8.0在安装应用的时候需要权限

8.0系统设置中不再提供是否允许安装未知来源的应用这个选项,所以在进行安装的时候,一定注意这个权限,否则会报错
其他注意事项:清单文件中必须包含provider

<provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="包名.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

res文件夹下建立xml文件夹
在xml中新建file_paths.xml文件,如下:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="external_storage_root"
        path="." />
</paths>

安装

 public static void installApp(final File file, final String authority) {
        if (!isFileExists(file)) return;
        Utils.getApp().startActivity(IntentUtils.getInstallAppIntent(file, authority, true));
    }
public static Intent getInstallAppIntent(final File file,
                                             final String authority,
                                             final boolean isNewTask) {
        if (file == null) return null;
        Intent intent = new Intent(Intent.ACTION_VIEW);
        Uri data;
        String type = "application/vnd.android.package-archive";
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            data = Uri.fromFile(file);
        } else {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            data = FileProvider.getUriForFile(Utils.getApp(), authority, file);
        }
        intent.setDataAndType(data, type);
        return getIntent(intent, isNewTask);
    }

布局也贴一下,根据自己的需求

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/white_alpha_6">
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/notify_download_iv"
        android:src="@mipmap/ic_launcher_round"
        android:layout_marginTop="12dp"
        android:layout_centerVertical="true"
        android:layout_marginRight="8dp"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/notify_tv"
        android:textColor="@color/black"
        android:layout_marginTop="8dp"
        android:layout_toRightOf="@+id/notify_download_iv"
        android:text="正在下载..."/>
    <ProgressBar
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/notify_progress_pb"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_below="@+id/notify_tv"
        android:layout_toRightOf="@+id/notify_download_iv"
        />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="12sp"
            android:textColor="@color/black"
            android:id="@+id/notify_progress_instruction_tv"
            android:layout_below="@+id/notify_progress_pb"
            android:text="客户端已经下载了"
            android:layout_toRightOf="@+id/notify_download_iv"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="12sp"
            android:id="@+id/notify_progress_tv"
            android:textColor="@color/black"
            android:layout_toRightOf="@+id/notify_progress_instruction_tv"
            android:layout_below="@+id/notify_progress_pb"
            android:text="50%"
            />

</RelativeLayout>

启动服务:

 /**
                         * 传入url
                         */
                        Intent intent = new Intent(MainActivity.this,UpdateService.class);
                        intent.putExtra("apkurl", updataVersionUrl);
                        startService(intent);

下载完成后,点击事件的处理,采用了BroadCastReceiver方式

public class NotificationBroadCast extends BroadcastReceiver {
    private File downFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/industry.apk");
    private static final String INSTALL_ACTION = "包名.click.toinstall";


    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals(INSTALL_ACTION)){
            int notifyId = intent.getIntExtra("notifyId", 0);
            NotificationManager manager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
            manager.cancel(notifyId);
            if (FileUtils.isFileExists(downFile) && downFile.length() > 0) {
                AppUtils.installApp(downFile, "gaoxin.com.inforindustry.fileprovider");
            }
        }
    }
}

最后不要忘记在清单文件中注册一下,

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值