随着android 7.0的普及android 8.0的也逐渐流行起来,那么google对权限方面又有了新的修改。而且我发现在android8.0中除了一些bug,比如说:在小米6(Android 8.0系统)中无法自动安装的问题,那么之前做的更新系统就要稍微调整下。

那根据android 8.0我们重新理一下更新的思路:

1、写一个接口调用是否有更新,可以读取版本号后台判断是否有更新,也可拉去最新信息,app内部判断:

2、创建网络监听,监听网络变化,在application中初始化,

3、创建更新service,根据service的机制,我们可以创建一个service不销毁,调用startService方法,service无论你创建多少次onCreate方法只会执行一次(当你没有stop的时候),而onStartCommand可以多次调用

4、调用更新接口,在回调中判断是否需要更新,如果需要更新就创建更新BroadcastReceiver广播,用来通知更新的开始,进度更新,结束,和各种失败的情况,然后创建更新提示dialog(这个不用我说了吧),为了防止更新按钮多次点击和重复下载的问题,我使用了三个全局静态变量来控制

public static boolean hasNewAppVersion = false;//是否有新版本
    public static boolean isDownLoadApk = false;//apk是否正在下载
    public static boolean isSuccessRequestUpdate = false;//更新请求是否成功了

5.点击更新弹框的,更新按钮,创建更新下载的service,注意下载需要在子线程中做。同时根据下载的开始,下载进度,或者结束,失败等情况利用广播器进行通知。

6、下载完成调用安装,这里一定要注意,android O更新了未知来源的权限,需要在配置文件中添加配置

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

那么接下来我们一步一步讲解

一、接口调用是否有更新,返回数据如下大致如下:

{
"status":true,
"code":200,
"data":{
"VersionName":"1.1.8",
"VersionCode":19,
"AppUrl":"https://********/appfile/*****.apk",//更新地址
"Content":"部分BUG修复",//提示内容
"Status":1,
"isMustUpdate":true//是否强制更新
},
"msg":"返回成功"
}
二、网络状态监听

创建网络状态变化广播器

/**
 * Created by CherishTang on 2016/12/29.
 * 自定义广播接收器,监听网络状态是否发生改变
 */
public class NetworkStateReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (NetworkStateUtil.isNetWorkConnected(context) && NetworkStateUtil.isWifiConnected(context)) {
            WifiInfo wifiInfo = NetworkStateUtil.getWifiInfo(context);
//            MThoast.showShort(context, "已连接到"+(wifiInfo.getSSID()==null?"wifi":wifiInfo.getSSID())+"网络");
            processCustomMessage(context, 1);
        } else if (NetworkStateUtil.isNetWorkConnected(context) && NetworkStateUtil.isMobileConnected(context)) {
//            MThoast.showShort(context, "已连接到数据流量网络");
            processCustomMessage(context, 2);
        } else {
            StaticUtil.isDownLoadApk = false;
            processCustomMessage(context, 3);
//            MThoast.showShort(context, "已进入无网络次元,请检查网络设置!");
        }
    }

//send msg to MainActivity
    private void processCustomMessage(Context context, Integer message) {
//        Intent mIntent = new Intent(BaseActivity.ACTION_INTENT_RECEIVER_MESSAGE);
//        mIntent.putExtra("message", message);
//        context.sendBroadcast(mIntent);
        if (networkChangeListener != null) {
            networkChangeListener.networkChanger(message);
        }
    }

private NetworkChangeListener networkChangeListener;

public void setNetworkChangeListener(NetworkChangeListener networkChangeListener) {
        this.networkChangeListener = networkChangeListener;
    }
}
添加网络状态变化监听权限

<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
为了防止重复注册我们再application中注册广播

public final static String ACTION_INTENT_RECEIVER ="android.net.conn.CONNECTIVITY_CHANGE";
public final static String ACTION_INTENT_RECEIVER_8 = "android.net.wifi.SCAN_RESULTS";

/**
* 注册广播,在onCreate中调用
*/
private void initNetWorkReceiver() {
intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_INTENT_RECEIVER);
intentFilter.addAction(ACTION_INTENT_RECEIVER_8);
networkChangeReceiver = new NetworkStateReceiver();
registerReceiver(networkChangeReceiver, intentFilter);
}

/**
* 回收广播
*/
@Override
public void onTerminate() {
super.onTerminate();
if (networkChangeReceiver != null)
unregisterReceiver(networkChangeReceiver);
}

/**
* 获取NetworkStateReceiver 实例
*/

public NetworkStateReceiver getNetworkChangeReceiver(){
return networkChangeReceiver;
}
三、创建更新service

在你的MainActivity中开启更新service,同时监听网络变化状态,在onStartConmmand中检查给更新

四、调用接口检查更新

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

checkVersionUpdate();//检查更新网络请求

//获取网络状态变化广播器
NetworkStateReceiver networkStateReceiver =
MyApplication.getInstance().getNetworkChangeReceiver();

//如果广播器不为空,在添加网络变化监听回调
if (networkStateReceiver != null)
networkStateReceiver.setNetworkChangeListener(this);

return super.onStartCommand(intent, flags, startId);
}

/**
* 注册更新BroadcastReceiver
*
* @param versionMessage 更新数据
*/
public void updateCheck(VersionMessage versionMessage) {
StaticUtil.hasNewAppVersion = true;

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(DownloadApkService.ACTION_START);
intentFilter.addAction(DownloadApkService.ACTION_UPDATE);
intentFilter.addAction(DownloadApkService.ACTION_FINISHED);
intentFilter.addAction(DownloadApkService.ACTION_CANCEL);
intentFilter.addAction(DownloadApkService.ACTION_ERROR);
intentFilter.addAction(DownloadApkService.ACTION_REDIRECT_ERROR);

downloadApkReceiver = new DownloadApkReceiver();//创建更新下载广播器
registerReceiver(downloadApkReceiver, intentFilter);
//获取当前activity
Activity mContext = AppManager.getAppManager().currentActivity();

//如果程序在前台,并且当前activity没有销毁的话,创建更新弹框,
//我封装在UpdateManger方法中了
if (AppManager.isForeground(this) && mContext != null) {
UpdateManger.getInstance().checkUpdateInfo(mContext,
versionMessage.getAppUrl(), versionMessage.getContent(),
versionMessage.getVersionName(), versionMessage.getMustUpdate());
}
}
五、点击更新按钮下载新版app

首先判断任务是否正在下载,避免重复下载

if (StaticUtil.isDownLoadApk) {
MThoast.showShort(mContext, "已存在下载任务,请勿重复下载");
return;
}
检查并动态获取文件的读取权限

if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
//申请WRITE_EXTERNAL_STORAGE权限
ActivityCompat.requestPermissions(mContext, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
0x01);
return;
}
创建更新service并关闭更新提示框

if (customDialog != null && customDialog.isShowing())
customDialog.dismiss();
Intent intent = new Intent(mContext, DownloadApkService.class);
intent.setAction(DownloadApkService.ACTION_START);
intent.putExtra("id", 0);
intent.putExtra("url", apkUrl);
intent.putExtra("name", "保存到本地的app名称");
mContext.startService(intent);
更新下载service代码:

/**
* Created by CherishTang on 2017/10/17.
* 更新app服务
*/

public class DownloadApkService extends Service {

public static final String ACTION_START = "ACTION_START";
public static final String ACTION_UPDATE = "ACTION_UPDATE";
public static final String ACTION_FINISHED = "ACTION_FINISHED";
public static final String ACTION_CANCEL = "ACTION_CANCEL";
public static final String ACTION_ERROR = "ACTION_ERROR";
public static final String ACTION_REDIRECT_ERROR = "ACTION_REDIRECT_ERROR";
public static final String HIDE_DIALOG = "HIDE_DIALOG";

// 文件的保存路径
public static final String path = Environment.getExternalStorageDirectory().getAbsolutePath() +
File.separator + StaticUtil.ROOTFILEPATH + File.separator + "download" + File.separator;

@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
if (ACTION_START.equals(intent.getAction())) {
new DownLoadApkThread(intent.getIntExtra("id", 0)
, intent.getStringExtra("url"), intent.getStringExtra("name")).start();
}
}
return super.onStartCommand(intent, flags, startId);
}

public class DownLoadApkThread extends Thread {

private int id;
private String downloadUrl;
private String fileName;

public DownLoadApkThread(int id, String downloadUrl, String fileName) {
this.id = id;
this.downloadUrl = downloadUrl;
this.fileName = fileName;

}

@Override
public void run() {
HttpURLConnection connLength = null;
HttpURLConnection connFile = null;
RandomAccessFile randomAccessFile = null;
InputStream inputStream = null;
URL url = null;
try {
url = new URL(downloadUrl);
//获取apk文件长度
connLength = (HttpURLConnection) url.openConnection();
connLength.setConnectTimeout(5000);
connLength.setRequestMethod("GET");
int code = connLength.getResponseCode();
int length = 0;
if (code == HttpURLConnection.HTTP_OK) {
length = connLength.getContentLength();
} else if (code == 301) {

sendBroadcast(new Intent().setAction(ACTION_REDIRECT_ERROR));
return;

} else {
sendBroadcast(new Intent().setAction(ACTION_ERROR));
return;
}

//判断文件是否存在,不存在则创建
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, fileName);
randomAccessFile = new RandomAccessFile(file, "rwd");
randomAccessFile.setLength(length);

//下载文件
connFile = (HttpURLConnection) url.openConnection();
connFile.setConnectTimeout(5000);
connFile.setRequestMethod("GET");
connFile.setRequestProperty("Range", "bytes=" + 0 + "-" + length);
code = connFile.getResponseCode();

//显示通知栏进度条
Intent intent = new Intent();
intent.setAction(ACTION_START);
intent.putExtra("id", id);
sendBroadcast(intent);

if (code == HttpURLConnection.HTTP_PARTIAL) {
inputStream = connFile.getInputStream();
int finished = 0;
byte[] bytes = new byte[1024 * 1024];
int len = -1;
long time = System.currentTimeMillis();
while ((len = inputStream.read(bytes)) != -1) {
//文件写入
randomAccessFile.write(bytes, 0, len);
//更新通知栏进度条
finished += len;
if (System.currentTimeMillis() - time > 1000) {
time = System.currentTimeMillis();
intent.setAction(ACTION_UPDATE);
int pro = (int) (((float) finished / length) * 100);
intent.putExtra("finished", pro);
sendBroadcast(intent);
}
}
}
//关闭通知栏
intent.setAction(ACTION_FINISHED);
sendBroadcast(intent);

} catch (MalformedURLException e) {
sendBroadcast(new Intent().setAction(ACTION_ERROR));
e.printStackTrace();
} catch (IOException e) {
sendBroadcast(new Intent().setAction(ACTION_ERROR));
e.printStackTrace();
} finally {
if (connLength != null) {
connLength.disconnect();
}
if (connFile != null) {
connFile.disconnect();
}
try {
if (inputStream != null) {
inputStream.close();
}
if (randomAccessFile != null) {
randomAccessFile.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
super.run();
}
}
}
更新下载通知广播:

/**
* Created by 方舟 on 2017/10/17.
* 更新下载广播接收器
*/

public class DownloadApkReceiver extends BroadcastReceiver {

private UpdateNotificationUtil mNotificationUtil;

@Override
public void onReceive(Context context, Intent intent) {
mNotificationUtil = new UpdateNotificationUtil(context);

if (DownloadApkService.ACTION_START.equals(intent.getAction())) {
StaticUtil.isDownLoadApk = true;//单例下载,防止多任务进行
// 下载开始的时候启动通知栏
mNotificationUtil.showNotification(intent.getIntExtra("id", 0));
} else if (DownloadApkService.ACTION_UPDATE.equals(intent.getAction())) {
// 更新进度条
mNotificationUtil.updateNotification(intent.getIntExtra("id",0), intent.getIntExtra("finished", 0));
} else if (DownloadApkService.ACTION_FINISHED.equals(intent.getAction())) {
StaticUtil.isDownLoadApk = false;//变更未任务未下载
mNotificationUtil.cancelNotification(intent.getIntExtra("id", 0));// 下载结束后取消通知
UpdateManger.installApk(context, new File(DownloadApkService.path + StaticUtil.apkName));
} else if (DownloadApkService.ACTION_CANCEL.equals(intent.getAction())) {
StaticUtil.isDownLoadApk = false;//变更未任务未下载
mNotificationUtil.cancelNotification(intent.getIntExtra("id", 0));// 下载结束后取消通知
} else if (DownloadApkService.ACTION_ERROR.equals(intent.getAction())) {
MThoast.showShort(context, "读取文件失败,请前往官方网站扫码下载最新版本!");
}else if (DownloadApkService.ACTION_REDIRECT_ERROR.equals(intent.getAction())) {
MThoast.showShort(context, "下载地址重定向出现错误,请稍后再试!");
}
}
}
由于android 8.0添加了NotificationChannel的概念因此这个UpdateNotificationUtil工具类可以根据新的方式自己写,我贴一个仅供参考其中

StaticUtil.updateVerisonChannelId,StaticUtil.updateVerisonChannelName,两个静态变量最好不要重复

public static final String updateVerisonChannelId = "包名.updateVersionApp";
public static final String updateVerisonChannelName = "版本更新";
/**
* Created by CherishTang on 2017/10/13.
* 更新通知
*/
public class UpdateNotificationUtil {

private Context mContext;
private NotificationManager mManager;
private NotificationCompat.Builder mBuilder;

public UpdateNotificationUtil(Context context) {
this.mContext = context;
mManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationUtils.createNotificationChannel(
false,false,
StaticUtil.updateVerisonChannelId,
StaticUtil.updateVerisonChannelName,
NotificationManager.IMPORTANCE_DEFAULT);
}
mBuilder = new NotificationCompat.Builder(context, StaticUtil.updateVerisonChannelId);
}

/**
* 显示通知栏
*
* @param id
*/
public void showNotification(final int id) {

mBuilder.setTicker("正在下载");//Ticker是状态栏显示的提示
mBuilder.setOngoing(true);
mBuilder.setContentTitle("正在下载最新版本");
mBuilder.setProgress(100, 0, false);
mBuilder.setContentText(0 + "%");
mBuilder.setSmallIcon(R.mipmap.icon_launcher);
mBuilder.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.icon_launcher));//通知栏的大图标
Intent msgIntent = new Intent();
msgIntent.setClass(mContext, MainActivity.class);
msgIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 100, msgIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pendingIntent);//点击跳转
mManager.notify(id, mBuilder.build());
}

/**
* 取消通知栏通知
*/
public void cancelNotification(int id) {
mManager.cancel(id);
}

/**
* 更新通知栏进度条
*
* @param id 获取Notification的id
* @param progress 获取的进度
*/
public void updateNotification(int id, int progress) {
if (mBuilder != null) {
mBuilder.setTicker("开始下载");//Ticker是状态栏显示的提示
mBuilder.setOngoing(true);
mBuilder.setContentTitle("app名称");
mBuilder.setSmallIcon(R.mipmap.icon_launcher);
mBuilder.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.icon_launcher));//通知栏的大图标
mBuilder.setProgress(100, progress, false);
mBuilder.setContentText(progress + "%");
mManager.notify(id, mBuilder.build());
}
}
}
/**
* Created by CherishTang on 2018/7/19.
* 通知栏工具类
*/

public class NotificationUtils {
private static NotificationManager mManager;
private static NotificationCompat.Builder notification;
private String contentText, contentTitle, channelId;
private boolean autoCancel = true;
private static Application application;

public static void init(Application application) {
NotificationUtils.application = application;
mManager = (NotificationManager) application.getSystemService(NOTIFICATION_SERVICE);
}

public NotificationUtils Builder(String channelId) {
if (notification == null) {
notification = new NotificationCompat.Builder(application, channelId == null ? "chat" : channelId);
} else {
notification.setChannelId(channelId);
}
this.channelId = channelId;
return this;
}

public static NotificationManager getManager() {
return mManager;
}

public static NotificationCompat.Builder getNotification() {
return notification;
}

@TargetApi(Build.VERSION_CODES.O)
public static void createNotificationChannel(boolean isVibrate,
boolean hasSound,
String channelId,
String channelName,
int importance) {
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
NotificationManager notificationManager = (NotificationManager) application.getSystemService(
NOTIFICATION_SERVICE);
channel.enableVibration(isVibrate);
channel.enableLights(true);
if (!hasSound)
channel.setSound(null, null);
if (notificationManager != null)
notificationManager.createNotificationChannel(channel);
}

public NotificationUtils setChannelId(String channelId) {
notification.setChannelId(channelId);
this.channelId = channelId;
return this;
}

public NotificationUtils setContentText(String contentText) {
notification.setContentText(contentText);
this.contentText = contentText;
return this;
}

public NotificationUtils setContentTitle(String contentTitle) {
notification.setContentTitle(contentTitle);
this.contentTitle = contentTitle;
return this;
}

public NotificationUtils setAutoCancel(boolean autoCancel) {
notification.setAutoCancel(autoCancel);
this.autoCancel = autoCancel;
return this;
}

public NotificationUtils notifyMessage(int id) {
if (mManager != null) {
notification.setContentTitle(contentTitle)
.setContentText(contentText)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.icon_launcher)
.setLargeIcon(BitmapFactory.decodeResource(application.getResources(), R.mipmap.icon_launcher))
.setAutoCancel(autoCancel)
.build();
mManager.notify(id, notification.build());
}
return this;
}

}
六、下载完成后启动自动安装

首先注意在Android O新增权限,必须添加,最后调用安装方法

<!--适配android 8.0-->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
public static void installApk(Context mContext, File apkFile) {
try{
if (!apkFile.exists()) {
return;
}
Intent i = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= 24) { //适配安卓7.0
i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK); //添加这一句表示对目标应用临时授权该Uri所代表的文件
Uri apkFileUri = FileProvider.getUriForFile(mContext.getApplicationContext(),
mContext.getPackageName() + ".FileProvider", apkFile);
i.setDataAndType(apkFileUri, "application/vnd.android.package-archive");
} else {
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.setDataAndType(Uri.parse("file://" + apkFile.toString()),
"application/vnd.android.package-archive");// File.toString()会返
回路径信息
}
mContext.startActivity(i);
}catch (Exception e){
e.printStackTrace();
MThoast.showShort(mContext,"自动安装失败,请尝试手动安装!");
}
}
到这里版本更新就好了,大家有问题可以留言。

demo地址:https://download.csdn.net/download/fzkf9225/10679015

我做了两个下载进度条,通知栏和dialog,我觉得一般用通知栏就可以了,dialog最好不用如果关闭下载进度的dialog弹框的话,注销相关DownloadProgressDialog类的相关代码即可
---------------------
作者:青穗CherishTang
来源:CSDN
原文:https://blog.csdn.net/fzkf9225/article/details/80969439
版权声明:本文为博主原创文章,转载请附上博文链接!

android 版本更新适配8.0,解决8.0手机无法更新自动安装apk的更多相关文章

  1. 下载apk安装包后,调用安装器自动安装apk(适配7.0)

    在更新操作时,下载新apk之后,往往需要自动安装新apk,用以下代码即可安装下载在本地的apk文件(apkFile) Intent intent = new Intent(); intent.setA ...

  2. 命令行从Android手机中导出已安装APK的方法调研

    一.背景 二.步骤 一.背景 很多时候,APK文件只存在于应用市场,在PC上无法直接下载.用手机下载下来后就直接安装了,也不能保存原始的APK文件. APK安装到手机后,Android系统会保存一份和 ...

  3. Android实现应用下载并自动安装apk包

    安装: ? 1 2 3 4 5 String str = "/CanavaCancel.apk"; String fileName = Environment.getExterna ...

  4. 【我的Android进阶之旅】 解决bug: Expected file scheme in URI: content://downloads/my_downloads/12

    一.错误描述 今天测试MM用HTC手机测试某个模块的时候crash了,抓log后发现是使用DownloadManager下载apk安装包然后自动安装的时候,抛了异常:java.lang.Illegal ...

  5. android6.0、7.0、8.0新特性总结之开发应用时加以考虑的一些主要变更。

    android6.0 参考一:简书Android 6.0 新特性详解 参考二:关于Android6.0以上系统的权限问题 参考三:值得你关注的Android6.0上的重要变化(一) 参考四:值得你关注 ...

  6. Android 自己实现更新下载自动安装

    1.一些公司开发完一款App之后可能并不会去上架App商店,但事后期也需要定时进行维护更新,所以会选择把打包好的apk 发布到自己的服务器,然后在数据库建一个版本号的表,然后剩下的就交给你androi ...

  7. android 开发 实现自动安装

    场景:实现自动安装apk程序 注意:不能使用 intent.setDataAndType(Uri.parse(apkPath),  "application/vnd.android.pack ...

  8. Android权限管理之RxPermission解决Android 6.0 适配问题

    前言: 上篇重点学习了Android 6.0的运行时权限,今天还是围绕着Android 6.0权限适配来总结学习,这里主要介绍一下我们公司解决Android 6.0权限适配的方案:RxJava+RxP ...

  9. Android app 在线更新那点事儿(适配Android6.0、7.0、8.0)

    一.前言 app在线更新是一个比较常见需求,新版本发布时,用户进入我们的app,就会弹出更新提示框,第一时间更新新版本app.在线更新分为以下几个步骤: 1, 通过接口获取线上版本号,versionC ...

随机推荐

  1. MapServer Tutorial——MapServer7.2.1教程学习——第一节用例实践:Example1.5 Adding a raster layer

    MapServer Tutorial——MapServer7.2.1教程学习——第一节用例实践:Example1.5 Adding a  raster layer 一.前言 MapServer不仅支持 ...

  2. 浮点型数据转整型的丢失精度问题(C++)

    如下代码:http://ideone.com/xcgHgw #include <iostream> using namespace std; int main() { // your co ...

  3. phthon--------异常处理

    一 什么是异常 异常就是程序运行时发生错误的信号(在程序出现错误时,则会产生一个异常,若程序没有处理它,则会抛出该异常,程序的运行也随之终止),在python中,错误触发的异常如下 而错误分成两种 # ...

  4. java多线程面试中常见知识点

    1.进程和线程 (1)进程是资源分配的最小单位,线程是程序执行的最小单位. (2)进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段.堆栈段和数据段,这种操作非 ...

  5. linux使用npm成功安装命令后,执行时却报找不到命令的问题

    # 使用npm安装serve命令 ~$ npm install serve --global 安装成功 # 可是执行命令会报错 ~$ serve -v bash: serve: command not ...

  6. python enumerate用法总结

    enumerate()说明enumerate()是python的内置函数enumerate在字典上是枚举.列举的意思对于一个可迭代的(iterable)/可遍历的对象(如列表.字符串),enumera ...

  7. egret 添加帧动画

    private showEffect(): void { //加载本地的帧动画资源 RES.getResByUrlNoCache("resource/assets/shenqi_eff.js ...

  8. 关于Linux时间设置的总结

    系统环境:centos Linux的时间有两种,一种是系统时间,一种是硬件时间. 系统时间的查看:#date 系统时间的设置:#date -set 时间同步工具准备 Linux进行时间同步要使用一个工 ...

  9. Timer 的学习

    Timer 实例化多个对象就会启动多个线程 TimerTask 中 捕获异常为基类Exception,那么出现异常后就继续执行.及时报错 TimerTask中未捕获异常或者捕获异常与程序抛出异常不一致 ...

  10. tensorFlow可以运行的代码

    折腾了很久,终于运行成功. 才云科技的书不错,就是需要微调一二. 心得:1,记得activate tensorflow,然后再python 2,Python的代码格式很重要,不要错误. 3,还不清楚如 ...