Giter Site home page Giter Site logo

xuexiangjys / xupdate Goto Github PK

View Code? Open in Web Editor NEW
2.3K 37.0 404.0 40.67 MB

🚀A lightweight, high availability Android version update framework.(一个轻量级、高可用性的Android版本更新框架)

Home Page: https://github.com/xuexiangjys/XUpdate/wiki

License: Apache License 2.0

Java 100.00%
android update android-ui apk xupdate xui x-library

xupdate's Introduction

XUpdate

api I Star

A lightweight, high availability Android version update framework. Click instruction document to experience it!

Please read 【wisdom of asking questions】 before raising the issue and strictly follow the issue template fill in and save everyone's time.

Please read the instruction document carefully before use, important things are to be repeated for three time!!!

Please read the instruction document carefully before use, important things are to be repeated for three time!!!

Please read the instruction document carefully before use, important things are to be repeated for three time!!!

About me

WeChat public number juejin zhihu CSDN jianshu segmentfault bilibili toutiao
我的Android开源之旅 Click me Click me Click me Click me Click me Click me Click me

Simplify use

If you want to use xupdate faster, reduce the difficulty of integration, support breakpoint continuation download and other expansion functions, you can try to use XUpdateAPI.

Rapid integration of X-Library

In order to facilitate the rapid integration of X-Library, I provide a template project for your reference: https://github.com/xuexiangjys/TemplateAppProject


Features

  • It supports Post or Get request to check version, and supports user-defined network requests.

  • Only version update under WiFi is supported。

  • Support silent download (background update) and automatic version update.

  • The user-friendly version update prompt pop-up window is provided to customize the theme style.

  • It supports user-defined version update checker, parser, prompter, downloader, install listener and error listener.

  • Support MD5 file verification, version ignore, version forced update and other functions.

  • It supports the user-defined file verification method【MD5 verification by default】.

  • Support custom request API interface.

  • Compatible with Android 6.0 ~ 12.0.

  • Support Chinese and English language (internationalization).

  • Support the use of the flutter plugin:flutter_xupdate

  • Support the use of react native plugin:react-native-xupdate

Stargazers over time

Stargazers over time

Composition structure

This framework refers to AppUpdate some ideas and UI, the various parts of the version update are separated to form the following parts:

  • IUpdateChecker: Check for the latest version.

  • IUpdateParser: Parsing the data results returned by the server.

  • IUpdatePrompter: Display the latest version information.

  • IUpdateDownloader: Download the latest version of APK installation package.

  • IUpdateHttpService: The interface for network request.

In addition, there are two listeners:

  • OnUpdateFailureListener: Listening error

  • OnInstallListener: Callback of application installation

Update core manager:

  • IUpdateProxy: Responsible for the process control of version update, calling update to start the version update process.

Update process

Process after calling update:

IUpdateProxy/XUpdate --- (update) ---> IUpdateChecker --->(Request the server to get the latest version information)---> IUpdateParser ---> (Parse the data returned by the server, and build the UpdateEntity)---> IUpdateProxy ---> (If there is no latest version, end it directly, otherwise proceed to the following process)

    ---Automatic mode---> IUpdateDownloader ---> (Download the latest app apk) ---> Install application

    ---Non automatic mode---> IUpdatePrompter ---> Prompt for version update

                                                        ---> click Update ---> IUpdateDownloader ---> (Download the latest app apk) ---> Jump to application installation UI

                                                        ---> Click cancel or ignore ---> End

Click to view the framework UML design diagram


1、Demonstration

  • Default version update

xupdate_default.png

  • Background update

xupdate_background.png

  • Force version update

xupdate_force.png

  • Ignored version update

xupdate_ignore.png

  • Custom pop up theme

xupdate_custom.png

  • Use the system pop-up prompt

xupdate_system.png

Demo update background service

Because GitHub is slow to visit recently, if you need to experience xupdate better, you can Click to build a simple version update service.

Demo Download

Pgyer Download

Pgyer Download password: xuexiangjys

downloads

xupdate_download_pugongying.png

GitHub Download

downloads

xupdate_download.png


2、Quick integration guide

At present, it supports the use of the mainstream development tool AndroidStudio and add dependency by configures build.gradle directly.

2.1、Add gradle dependency

1.In the project root directory build.gradle:

allprojects {
     repositories {
        ...
        maven { url "https://jitpack.io" }
    }
}
  1. Then add in the dependencies of build.gradle of the application project (usually app):

The following is the version description. Choose one.

  • androidx project: above 2.0.0
dependencies {
  ...
  // androidx project
  implementation 'com.github.xuexiangjys:XUpdate:2.1.5'
}
  • support project: below 1.1.6
dependencies {
  ...
  // support project
  implementation 'com.github.xuexiangjys:XUpdate:1.1.6'
}

2.2、Initialization

Initialize settings at the top of the application:

XUpdate.get()
    .debug(true)
    .isWifiOnly(true)                                               // By default, only version updates are checked under WiFi
    .isGet(true)                                                    // The default setting uses Get request to check versions
    .isAutoMode(false)                                              // The default setting is non automatic mode
    .param("versionCode", UpdateUtils.getVersionCode(this))         // Set default public request parameters
    .param("appKey", getPackageName())
    .setOnUpdateFailureListener(new OnUpdateFailureListener() {     // Set listening for version update errors
        @Override
        public void onFailure(UpdateError error) {
            if (error.getCode() != CHECK_NO_NEW_VERSION) {          // Handling different errors
                ToastUtils.toast(error.toString());
            }
        }
    })
    .supportSilentInstall(true)                                     // Set whether silent installation is supported. The default is true
    .setIUpdateHttpService(new OKHttpUpdateHttpService())           // This must be set! Realize the network request function.
    .init(this);                                                    // This must be initialized

【note】: if there is any problem, you can open debug mode to track the problem. If you also need to log on disk, you can implement the following interface.

XUpdate.get().setILogger(new ILogger() {
    @Override
    public void log(int priority, String tag, String message, Throwable t) {
        // Realize the function of logging
    }
});

2.3、Version update entity information

(1) UpdateEntity

Field name Type Default value Description
mHasUpdate boolean false Whether have the latest version
mIsForce boolean false Force installation: app cannot be used without installation
mIsIgnorable boolean false Whether the version can be ignored
mVersionCode int 0 Latest version code
mVersionName String unknown_version Latest version name
mUpdateContent String "" Update content
mDownloadEntity DownloadEntity Download information entity
mIsSilent boolean false Whether to download silently: when there is a new version, do not prompt to download directly
mIsAutoInstall boolean true Whether to automatic install app when the download is completed

(2) DownloadEntity

Field name Type Default value Description
mDownloadUrl String "" Download address
mCacheDir String "" File download directory
mMd5 String "" The encrypted check value of the downloaded file (MD5 encryption is used by default), which is used to verify and prevent the downloaded APK file from being replaced (the latest demo has a tool for calculating the check value). Note that the MD5 value here is not the MD5 value of the application signature file!
mSize long 0 Size of download file【unit: KB】
mIsShowNotification boolean false Whether to show download progress in the notification bar

(3) PromptEntity

Field name Type Default value Description
mThemeColor int R.color.xupdate_default_theme_color Theme colors (background colors for progress bars and buttons)
mTopResId int R.drawable.xupdate_bg_app_top Top background image resource ID
mTopDrawableTag String "" Top background image drawable tag
mButtonTextColor int 0 Button text color
mSupportBackgroundUpdate boolean false Whether background updates are supported
mWidthRatio float -1(Unconstrained) The ratio of the width of the version update prompter to the screen
mHeightRatio float -1(Unconstrained) The ratio of the height of the version update prompter to the screen
mIgnoreDownloadError boolean false Whether to ignore the download exception (the update prompt box will not disappear if the download fails)

2.4、File encryption verification method

The default file encryption verification method used in this framework is MD5 encryption. Of course, if you don't want to use MD5 encryption, you can also customize the File Encryptor IFileEncryptor. The following is the implementation of MD5 File Encryptor for reference:

/**
 * The default file encryption calculation uses MD5 encryption
 *
 * @author xuexiang
 * @since 2019-09-06 14:21
 */
public class DefaultFileEncryptor implements IFileEncryptor {
    /**
     * Encrypted files
     *
     * @param file
     * @return
     */
    @Override
    public String encryptFile(File file) {
        return Md5Utils.getFileMD5(file);
    }

    /**
     * Verify that the file is valid (whether the encryption is consistent)
     *
     * @param encrypt The encrypted value, is considered to be valid if encrypt is empty.
     * @param file    File to be verified
     * @return Whether the document is valid
     */
    @Override
    public boolean isFileValid(String encrypt, File file) {
        return TextUtils.isEmpty(encrypt) || encrypt.equalsIgnoreCase(encryptFile(file));
    }
}

Finally, call the XUpdate.get().setIFileEncryptor method, settings will take effect.


3、Version update

3.1、Default version update

You can directly call the following code to complete the version update operation:

XUpdate.newBuild(getActivity())
        .updateUrl(mUpdateUrl)
        .update();

It should be noted that with the default version update, the JSON format returned by the request server should include the following contents:

{
  "Code": 0,
  "Msg": "",
  "UpdateStatus": 1,
  "VersionCode": 3,
  "VersionName": "1.0.2",
  "ModifyContent": "1、优化api接口。\r\n2、添加使用demo演示。\r\n3、新增自定义更新服务API接口。\r\n4、优化更新提示界面。",
  "DownloadUrl": "https://raw.githubusercontent.com/xuexiangjys/XUpdate/master/apk/xupdate_demo_1.0.2.apk",
  "ApkSize": 2048,
  "ApkMd5": ""
}

Field description:

  • Code: 0 means the request is successful, non-0 means failure.
  • Msg: Request error information.
  • UpdateStatus: 0 means no update, 1 means version update, no forced upgrade is required, and 2 represents version update and forced upgrade is required.
  • VersionCode: Version number, self incrementing. Used to compare whether the version is the latest version.
  • VersionName: Display name of version.
  • ModifyContent: Content of version update.
  • DownloadUrl: Download address of application APK file.
  • ApkSize: The file size of the application APK file, in KB.
  • ApkMd5: Apply the MD5 value of the APK file. If not, the APK cannot be guaranteed to be complete and will be downloaded again every time. The framework uses MD5 encryption by default.

3.2、Automatic version update

Automatic version update: auto check version + auto download APK + auto install APK (silent install).

You only need to set isAutoMode(true). However, if the device does not have root permission, it will not be able to complete automatic update (because the silent installation requires root permission).

XUpdate.newBuild(getActivity())
        .updateUrl(mUpdateUrl)
        .isAutoMode(true) // If you need to be completely unattended and update automatically, you need root permission【required for silent installation】
        .update();

3.3、Support background update

After enabling the background update, users can enter the background update after clicking the "background update" button.

XUpdate.newBuild(getActivity())
        .updateUrl(mUpdateUrl)
        .supportBackgroundUpdate(true)
        .update();

3.4、Force version update

If the user does not update, the program will not work normally. The server only needs to return the UpdateStatus field to 2.

Of course, if you customize the request return API, you only need to set the mIsForce field of UpdateEntity to true.

3.5、Custom version update prompt pop-up theme

By setting the update top picture, theme color, button text color, width to height ratio, etc

  • promptThemeColor: Set theme color
  • promptButtonTextColor: Set the text color of the button
  • promptTopResId: Set top background image Resource ID
  • promptTopDrawable: Set top background image drawable
  • promptTopBitmap: Set top background image bitmap
  • promptWidthRatio: Set the ratio of the width of the version update prompt to the screen. The default value is -1(No constraint is required).
  • promptHeightRatio: Set the ratio of the height of the version update prompt to the screen. The default value is -1(No constraint is required).
XUpdate.newBuild(getActivity())
        .updateUrl(mUpdateUrl)
        .promptThemeColor(ResUtils.getColor(R.color.update_theme_color))
        .promptButtonTextColor(Color.WHITE)
        .promptTopResId(R.mipmap.bg_update_top)
        .promptWidthRatio(0.7F)
        .update();

3.6、Custom version update parser

The implementation of IUpdateParser interface can realize the user-defined parser.

XUpdate.newBuild(getActivity())
        .updateUrl(mUpdateUrl3)
        .updateParser(new CustomUpdateParser()) // Set up a custom version update parser
        .update();

public class CustomUpdateParser implements IUpdateParser {
    @Override
    public UpdateEntity parseJson(String json) throws Exception {
        CustomResult result = JsonUtil.fromJson(json, CustomResult.class);
        if (result != null) {
            return new UpdateEntity()
                    .setHasUpdate(result.hasUpdate)
                    .setIsIgnorable(result.isIgnorable)
                    .setVersionCode(result.versionCode)
                    .setVersionName(result.versionName)
                    .setUpdateContent(result.updateLog)
                    .setDownloadUrl(result.apkUrl)
                    .setSize(result.apkSize);
        }
        return null;
    }
}

3.7、Custom version update checker + version update parser + version update prompter

  • By implementing the IUpdateChecker interface, the checker can be customized.

  • By implementing the IUpdateParser interface, the parser can be customized.

  • By implementing the IUpdatePrompter interface, the prompter can be customized.

XUpdate.newBuild(getActivity())
        .updateUrl(mUpdateUrl3)
        .updateChecker(new DefaultUpdateChecker() {
            @Override
            public void onBeforeCheck() {
                super.onBeforeCheck();
                CProgressDialogUtils.showProgressDialog(getActivity(), "查询中...");
            }
            @Override
            public void onAfterCheck() {
                super.onAfterCheck();
                CProgressDialogUtils.cancelProgressDialog(getActivity());
            }
        })
        .updateParser(new CustomUpdateParser())
        .updatePrompter(new CustomUpdatePrompter(getActivity()))
        .update();


public class CustomUpdatePrompter implements IUpdatePrompter {

    private Context mContext;

    public CustomUpdatePrompter(Context context) {
        mContext = context;
    }

    @Override
    public void showPrompt(@NonNull UpdateEntity updateEntity, @NonNull IUpdateProxy updateProxy, @NonNull PromptEntity promptEntity) {
        showUpdatePrompt(updateEntity, updateProxy);
    }

    /**
     * Show custom version update prompter
     *
     * @param updateEntity
     * @param updateProxy
     */
    private void showUpdatePrompt(final @NonNull UpdateEntity updateEntity, final @NonNull IUpdateProxy updateProxy) {
        String updateInfo = UpdateUtils.getDisplayUpdateInfo(mContext, updateEntity);

        new AlertDialog.Builder(mContext)
                .setTitle(String.format("是否升级到%s版本?", updateEntity.getVersionName()))
                .setMessage(updateInfo)
                .setPositiveButton("升级", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        updateProxy.startDownload(updateEntity, new OnFileDownloadListener() {
                            @Override
                            public void onStart() {
                                HProgressDialogUtils.showHorizontalProgressDialog(mContext, "下载进度", false);
                            }

                            @Override
                            public void onProgress(float progress, long total) {
                                HProgressDialogUtils.setProgress(Math.round(progress * 100));
                            }

                            @Override
                            public boolean onCompleted(File file) {
                                HProgressDialogUtils.cancel();
                                return true;
                            }

                            @Override
                            public void onError(Throwable throwable) {
                                HProgressDialogUtils.cancel();
                            }
                        });
                    }
                })
                .setNegativeButton("暂不升级", null)
                .setCancelable(false)
                .create()
                .show();
    }

3.8、Only use the downloader function to download APK

XUpdate.newBuild(getActivity())
        .apkCacheDir(PathUtils.getAppExtCachePath())  // Set the root directory of the download cache
        .build()
        .download(mDownloadUrl, new OnFileDownloadListener() {   // Set the download address and download listener
            @Override
            public void onStart() {
                HProgressDialogUtils.showHorizontalProgressDialog(getContext(), "下载进度", false);
            }

            @Override
            public void onProgress(float progress, long total) {
                HProgressDialogUtils.setProgress(Math.round(progress * 100));
            }

            @Override
            public boolean onCompleted(File file) {
                HProgressDialogUtils.cancel();
                ToastUtils.toast("apk下载完毕,文件路径:" + file.getPath());
                return false;
            }

            @Override
            public void onError(Throwable throwable) {
                HProgressDialogUtils.cancel();
            }
        });

3.9、Only use the APK installed features of XUpdate

_XUpdate.startInstallApk(getContext(), FileUtils.getFileByPath(PathUtils.getFilePathByUri(getContext(), data.getData()))); // Set the path where the file is located

If your APK installation is different, you can implement your own APK installer. You only need to implement the OnInstallListener interface and use the XUpdate.setOnInstallListener set it to take effect.

3.10、International

Due to the limited level of the author, only Chinese and English are supported【the default language is English】. If you need to support other languages, you only need to create a new corresponding language file under the res of your own project to translate in multiple languages.

Please refer to xupdate_strings.xml

What? You don't know Android's multilingual configuration yet? I suggest you take a look at this article:Android项目国际化多国语言适配

Proguard

-keep class com.xuexiang.xupdate.entity.** { *; }

// Note: if you use a custom API parser for parsing, you need to add confusion to your custom API entities. Here are the custom API entity obfuscation rules configured in this demo:
-keep class com.xuexiang.xupdatedemo.entity.** { *; }

Related links


Thanks

https://github.com/WVector/AppUpdate

Sponsor

Your support is the driving force of my maintenance. I will list the list of all the reward personnel at the bottom as the voucher. Please leave the notes of the support items before rewarding!

pay.png

Thank you for your sponsorship:

Name Money Platform
*天 100¥ WeChat
*航 10¥ Alipay
X*? 18.88¥ WeChat
*网 1¥ WeChat
Joe 88.88¥ WeChat

WeChat Subscription

More information, please scan my personal WeChat Subscription:【我的Android开源之旅】

Contact

xupdate's People

Contributors

xuexiangjys avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xupdate's Issues

sdk8.0以上的权限问题

首先谢谢你的作品,在实际使用中有以下问题反馈:
一、静默安装:在华为手机中直接服务被杀了,建议不开启静默安装的时候不要做root权限的检查
二、8.0以上没有申请REQUEST_INSTALL_PACKAGES权限的检查
三、建议下载地址不要二次解析,很多时候为了减轻服务器的压力,下载源都是放到oss上面去的

Attempt to invoke interface method 'void com.xuexiang.xupdate.proxy.e.h()' on a null object reference

java.lang.NullPointerException: Attempt to invoke interface method 'void com.xuexiang.xupdate.proxy.e.h()' on a null object reference
at com.xuexiang.xupdate.widget.UpdateDialogFragment.onClick(UpdateDialogFragment.java)
at android.view.View.performClick(View.java:5642)
at android.view.View$PerformClick.run(View.java:22489)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6278)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:793)

版本检测耦合性太高

如果想实现由Retrofit获取版本信息,然后弹出提示框后面的动作使用库中的内容,现在暂时不能实现。

这框架压根不能用

1:后台接口给的json串,按照你的方式一点都不行。
2:文档问题,后台数据跟接口完全对不上。

弹出更新提示框后,摁Home

弹出更新提示框后,摁Home键回到手机主页面,报如下错误:
和是否开始下载无关,和是否强制更新无关。在华为meta20(9.0)和坚果Pro2s(8.1)上都报这个错。

java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = **com.xuexiang.xupdate.entity.UpdateEntity**)
    at android.os.Parcel.writeSerializable(Parcel.java:1714)
    at android.os.Parcel.writeValue(Parcel.java:1662)
    at android.os.Parcel.writeArrayMapInternal(Parcel.java:875)
    at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1583)

下载完成后会有警告

查看log信息会发现有如下警告
java.io.IOException: Cannot run program "su": error=13, Permission denied
但是应用安装界面能够正常弹出

获取的下载文件名称有特殊字符的话无法安装,建议过滤下特殊字符

/**
* 根据下载地址获取文件名
*
* @param downloadUrl
* @return
/
@nonnull
public static String getApkNameByDownloadUrl(String downloadUrl) {
if (TextUtils.isEmpty(downloadUrl)) {
return "temp.apk";
} else {
String appName = downloadUrl.substring(downloadUrl.lastIndexOf("/") + 1, downloadUrl.length());
if (!appName.endsWith(".apk")) {
appName = "temp.apk";
}
return appName;
}
}
/
*
* 过滤文件名非法字符
/
private static Pattern FilePattern = Pattern.compile("[\\/:
?"<>|]");

public static String filenameFilter(String str) {
    return str == null ? null : FilePattern.matcher(str).replaceAll("");
}

apkCacheDir设置为内部存储(/data/data/package/files)时,安装时提示包解析出错。

当apkCacheDir设置为内部存储(比如:/data/data/package/files)时,安装时提示包解析遇到问题;设置为外部存储时(/storage/emulated/0/Download)时,安装包解析正常,可以安装成功。在网上查了下说apk文件权限不够,设置安装监听后,在安装前修改文件权限后,还是提示解析出错。
XUpdate.get().setOnInstallListener(new OnInstallListener() {
@OverRide
public boolean onInstallApk(@nonnull Context context, @nonnull File apkFile, @nonnull DownloadEntity downloadEntity) {
String command = "chmod -R 777 " + apkFile.getAbsolutePath();
ShellUtils.execCmd(command, true);
AppUtils.installApp(apkFile);
return true;
}

请问一下在reactnative 中怎么使用

我是在reactNative中使用的,在MainActivity使用
XUpdate.newBuild(MainActivity.this)
.updateUrl(mUpdateUrl)
.update();
报错: 使用默认的版本更新提示器,context必须传FragmentActivity!
不会安卓只是想在集成在我的reactNative 应用中使用,谢谢

咨询大大两个使用过程中的问题

首先感谢写了这么好的更新框架,在使用过程中我遇到了两个问题,想请大大提点一下。
1、开启了debug(true),但是好像日志中没有发现调试日志。
2、软件正常完成了版本更新,但是在新版APP还是跳出了升级版本的提示。(更新后APP的versionCode,versionName和远程更新服务器返回的json中的versionCode,versionName字段已经一致了)
Application文件

        XUpdate.get()
                .isWifiOnly(false)     //默认设置只在wifi下检查版本更新
                .isGet(true)          //默认设置使用get请求检查版本
                .isAutoMode(false)    //默认设置非自动模式,可根据具体使用配置
                .param("versionCode", UpdateUtils.getVersionCode(this)) //设置默认公共请求参数
                .param("appKey", getPackageName())
                .debug(true)
                .setOnUpdateFailureListener(new OnUpdateFailureListener() { //设置版本更新出错的监听
                    @Override
                    public void onFailure(UpdateError error) {
                        Log.d("application",error.toString());
                    }
                })
                .setIUpdateHttpService(new OKHttpUpdateHttpService()) //这个必须设置!实现网络请求功能。
                .init(this);   //这个必须初始化

Activity文件

        XUpdate.newBuild(this)
                .updateUrl(updateurl)
                .updateParser(new CustomUpdateParser())
                .update();

不太容易配置和成功

依赖太多了,想试验一下都没有成功。
先是:More than one file was found with OS independent path 'META-INF/rxjava.properties
后来:Couldn't load memtrack module //这个可能是那个依赖库没有加引起的。
不过最终配成功了。
XHttp2
这个依赖较多。开始没有加上 rxjava,rxandroid,总是闪退。
okhttp
这个依赖少一点。

总算成功了,谢谢。

自定义解析器下载更新

自定义解析器强更下载安装后,后台再次上传一个强更新版本点击检测更新一直弹的是选更的弹框,自定义实体解析 if (result != null) {
return new UpdateEntity()
.setHasUpdate(isupdate)
.setIsIgnorable(false)
.setForce(isForce)
.setVersionCode(result.getVersionCode())
.setVersionName(result.getVersionName())
.setUpdateContent(result.getUpdateIntro())
.setDownloadUrl(result.getFileName())
.setSize(1000);
}这个一直没走,安装新版本之前会走到这里,安装后有新版本就没走了

自定义版本更新解析器,在代码执行中Gson报错

具体位置是在(UpdateManager类的findNewVersion方法中,第288行),都是执行UpdateUtils.toJson(updateEntity)方法时报错。

坚果Pro2s(系统8.1),报如下错误:
2019-03-30 00:09:20.538 W/System.err: java.lang.IllegalArgumentException: class android.content.res.ColorStateList declares multiple JSON fields named mChangingConfigurations
2019-03-30 00:09:20.539 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:172)
2019-03-30 00:09:20.539 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
2019-03-30 00:09:20.539 W/System.err: at com.google.gson.Gson.getAdapter(Gson.java:458)
2019-03-30 00:09:20.539 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
2019-03-30 00:09:20.539 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
2019-03-30 00:09:20.539 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
2019-03-30 00:09:20.539 W/System.err: at com.google.gson.Gson.getAdapter(Gson.java:458)
2019-03-30 00:09:20.539 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
2019-03-30 00:09:20.539 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
2019-03-30 00:09:20.539 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
2019-03-30 00:09:20.539 W/System.err: at com.google.gson.Gson.getAdapter(Gson.java:458)
2019-03-30 00:09:20.539 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
2019-03-30 00:09:20.539 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
2019-03-30 00:09:20.539 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.Gson.getAdapter(Gson.java:458)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.Gson.getAdapter(Gson.java:458)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:56)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.Gson.toJson(Gson.java:704)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.Gson.toJson(Gson.java:683)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.Gson.toJson(Gson.java:638)
2019-03-30 00:09:20.540 W/System.err: at com.google.gson.Gson.toJson(Gson.java:618)
2019-03-30 00:09:20.540 W/System.err: at com.xuexiang.xupdate.utils.UpdateUtils.toJson(UpdateUtils.java:179)
2019-03-30 00:09:20.540 W/System.err: at com.xuexiang.xupdate.UpdateManager.findNewVersion(UpdateManager.java:288)
2019-03-30 00:09:20.540 W/System.err: at com.xuexiang.xupdate.proxy.impl.DefaultUpdateChecker.processCheckResult(DefaultUpdateChecker.java:132)
2019-03-30 00:09:20.540 W/System.err: at com.xuexiang.xupdate.proxy.impl.DefaultUpdateChecker.onCheckSuccess(DefaultUpdateChecker.java:102)
2019-03-30 00:09:20.540 W/System.err: at com.xuexiang.xupdate.proxy.impl.DefaultUpdateChecker.access$000(DefaultUpdateChecker.java:46)
2019-03-30 00:09:20.540 W/System.err: at com.xuexiang.xupdate.proxy.impl.DefaultUpdateChecker$2.onSuccess(DefaultUpdateChecker.java:77)
2019-03-30 00:09:20.540 W/System.err: at com.xxx.update.UpdateHttpService$1.onResponse(UpdateHttpService.java:46)
2019-03-30 00:09:20.540 W/System.err: at com.xxx.update.UpdateHttpService$1.onResponse(UpdateHttpService.java:43)
2019-03-30 00:09:20.540 W/System.err: at com.xxx.network.rx.RespObserver.onNext(RespObserver.java:156)
2019-03-30 00:09:20.541 W/System.err: at io.reactivex.internal.util.HalfSerializer.onNext(HalfSerializer.java:107)
2019-03-30 00:09:20.541 W/System.err: at io.reactivex.internal.operators.observable.ObservableTakeUntil$TakeUntilMainObserver.onNext(ObservableTakeUntil.java:79)
2019-03-30 00:09:20.541 W/System.err: at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:201)
2019-03-30 00:09:20.541 W/System.err: at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:255)
2019-03-30 00:09:20.541 W/System.err: at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:119)
2019-03-30 00:09:20.541 W/System.err: at android.os.Handler.handleCallback(Handler.java:790)
2019-03-30 00:09:20.541 W/System.err: at android.os.Handler.dispatchMessage(Handler.java:99)
2019-03-30 00:09:20.541 W/System.err: at android.os.Looper.loop(Looper.java:164)
2019-03-30 00:09:20.541 W/System.err: at android.app.ActivityThread.main(ActivityThread.java:6906)
2019-03-30 00:09:20.541 W/System.err: at java.lang.reflect.Method.invoke(Native Method)
2019-03-30 00:09:20.541 W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
2019-03-30 00:09:20.541 W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:820)

华为Meta20(系统9.0),报如下错误:
2019-03-30 00:22:05.233 W/System.err: java.lang.IllegalArgumentException: class android.widget.TextView declares multiple JSON fields named mMinWidth
2019-03-30 00:22:05.233 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:172)
2019-03-30 00:22:05.234 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
2019-03-30 00:22:05.234 W/System.err: at com.google.gson.Gson.getAdapter(Gson.java:458)
2019-03-30 00:22:05.235 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
2019-03-30 00:22:05.235 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
2019-03-30 00:22:05.235 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
2019-03-30 00:22:05.236 W/System.err: at com.google.gson.Gson.getAdapter(Gson.java:458)
2019-03-30 00:22:05.236 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:117)
2019-03-30 00:22:05.237 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:166)
2019-03-30 00:22:05.237 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:102)
2019-03-30 00:22:05.237 W/System.err: at com.google.gson.Gson.getAdapter(Gson.java:458)
2019-03-30 00:22:05.238 W/System.err: at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:56)
2019-03-30 00:22:05.238 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127)
2019-03-30 00:22:05.239 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245)
2019-03-30 00:22:05.239 W/System.err: at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
2019-03-30 00:22:05.239 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127)
2019-03-30 00:22:05.240 W/System.err: at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245)
2019-03-30 00:22:05.240 W/System.err: at com.google.gson.Gson.toJson(Gson.java:704)
2019-03-30 00:22:05.241 W/System.err: at com.google.gson.Gson.toJson(Gson.java:683)
2019-03-30 00:22:05.241 W/System.err: at com.google.gson.Gson.toJson(Gson.java:638)
2019-03-30 00:22:05.241 W/System.err: at com.google.gson.Gson.toJson(Gson.java:618)
2019-03-30 00:22:05.242 W/System.err: at com.xuexiang.xupdate.utils.UpdateUtils.toJson(UpdateUtils.java:179)
2019-03-30 00:22:05.242 W/System.err: at com.xuexiang.xupdate.UpdateManager.findNewVersion(UpdateManager.java:288)
2019-03-30 00:22:05.243 W/System.err: at com.xuexiang.xupdate.proxy.impl.DefaultUpdateChecker.processCheckResult(DefaultUpdateChecker.java:132)
2019-03-30 00:22:05.243 W/System.err: at com.xuexiang.xupdate.proxy.impl.DefaultUpdateChecker.onCheckSuccess(DefaultUpdateChecker.java:102)
2019-03-30 00:22:05.243 W/System.err: at com.xuexiang.xupdate.proxy.impl.DefaultUpdateChecker.access$000(DefaultUpdateChecker.java:46)
2019-03-30 00:22:05.244 W/System.err: at com.xuexiang.xupdate.proxy.impl.DefaultUpdateChecker$2.onSuccess(DefaultUpdateChecker.java:77)
2019-03-30 00:22:05.244 W/System.err: at com.xxx.update.UpdateHttpService$1.onResponse(UpdateHttpService.java:46)
2019-03-30 00:22:05.245 W/System.err: at com.xxx.update.UpdateHttpService$1.onResponse(UpdateHttpService.java:43)
2019-03-30 00:22:05.245 W/System.err: at com.xxx.network.rx.RespObserver.onNext(RespObserver.java:156)
2019-03-30 00:22:05.245 W/System.err: at io.reactivex.internal.util.HalfSerializer.onNext(HalfSerializer.java:107)
2019-03-30 00:22:05.246 W/System.err: at io.reactivex.internal.operators.observable.ObservableTakeUntil$TakeUntilMainObserver.onNext(ObservableTakeUntil.java:79)
2019-03-30 00:22:05.246 W/System.err: at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:201)
2019-03-30 00:22:05.247 W/System.err: at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:255)
2019-03-30 00:22:05.247 W/System.err: at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:119)
2019-03-30 00:22:05.247 W/System.err: at android.os.Handler.handleCallback(Handler.java:891)
2019-03-30 00:22:05.248 W/System.err: at android.os.Handler.dispatchMessage(Handler.java:102)
2019-03-30 00:22:05.248 W/System.err: at android.os.Looper.loop(Looper.java:207)
2019-03-30 00:22:05.248 W/System.err: at android.app.ActivityThread.main(ActivityThread.java:7470)
2019-03-30 00:22:05.249 W/System.err: at java.lang.reflect.Method.invoke(Native Method)
2019-03-30 00:22:05.249 W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
2019-03-30 00:22:05.249 W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:958)

安装失败

麻烦问下,如何查看安装失败具体错误码,现在只能看到自定义的5000

XUpdate.get().init() 必须传入Application

感谢作者,我并不是原生的Android开发者,我使用的是react-native来创建我的项目,我写了一个react-native插件来封装你的update功能,但是init方法需要接受application,导致我的封装产生了一些问题。我看过你的源代码,其实传入context对象也是一样的,我不得不fork一份你的代码,以便于在我的项目中使用。可否将这个修改一下。感谢

HasUpdate这个字段

HasUpdate这个字段是不是直接用apk的versionCode和请求到的versionCode比较下?

两个小建议

1:忽略当前版本建议根据VersionCode,小版本更新name有时候会懒的改,code绝对不会
2:如果允许强制更新,.supportBackgroundUpdate(true)建议不可用,因为用户可以后台更新 然后不安装继续使用旧版本

进度条不动

进度条一直显示不动,点升级,但文件在下载

每次调用升级都会去下载apk

每次启动我都会去调用升级,我看到log中每次都会下载apk。
如果用户不安装,或者其他情况,这样每次下载cdn流量开销很大。
我现在只能自己去保存上次下载文件路径,然后在下次更新检测的时候判断md5是否一样,
不一样就重新现在,一样就不下载。
看看有没有办法优化一下。
谢谢啊

setFlags 应该改为 addFlags

public static Intent getInstallAppIntent(Context context, File appFile) {
    try {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            //区别于 FLAG_GRANT_READ_URI_PERMISSION 跟 FLAG_GRANT_WRITE_URI_PERMISSION, URI权限会持久存在即使重启,直到明确的用 revokeUriPermission(Uri, int) 撤销。 这个flag只提供可能持久授权。但是接收的应用必须调用ContentResolver的takePersistableUriPermission(Uri, int)方法实现
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
            Uri fileUri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".updateFileProvider", appFile);
            intent.setDataAndType(fileUri, "application/vnd.android.package-archive");
        } else {
            intent.setDataAndType(Uri.fromFile(appFile), "application/vnd.android.package-archive");
        }
        return intent;
    } catch (Exception e) {
        _XUpdate.onUpdateError(INSTALL_FAILED, "获取安装的意图失败!");
    }
    return null;
}

上面这段代码中的 setFlags 应该改为 addFlags ,否则在 (SDK >= N && context 不是 Activity) 的情况下会导致打不开安装界面

why i can't set post json format to server ?

private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
RequestBody body = RequestBody.create(JSON, gson.toJson(param));
i often use okhttp3 like above setting format for transfer ,how about xupdate do it ?

能否直接跳过服务器请求?

我想直接跳过服务器请求,直接传入更新数据调出dialog。数据请求因为里面还有其他逻辑判断,必须我先处理完了才能确定是否调用升级。感谢回复

华为手机更新完后,不安装

: java.io.IOException: Cannot run program "su": error=13, Permission denied
W/System.err: at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
W/System.err: at java.lang.Runtime.exec(Runtime.java:692)
W/System.err: at java.lang.Runtime.exec(Runtime.java:525)
W/System.err: at java.lang.Runtime.exec(Runtime.java:422)
W/System.err: at freight.zengke.com.freight.widegt.appupdate.utils.ShellUtils.execCommand(ShellUtils.java:135)
W/System.err: at freight.zengke.com.freight.widegt.appupdate.utils.ShellUtils.execCommand(ShellUtils.java:93)
W/System.err: at freight.zengke.com.freight.widegt.appupdate.utils.ShellUtils.checkRootPermission(ShellUtils.java:44)
W/System.err: at freight.zengke.com.freight.widegt.appupdate.utils.ApkInstallUtils.install(ApkInstallUtils.java:65)
W/System.err: at freight.zengke.com.freight.widegt.appupdate.utils.ApkInstallUtils.install(ApkInstallUtils.java:52)
W/System.err: at freight.zengke.com.freight.widegt.appupdate.listener.impl.DefaultInstallListener.onInstallApk(DefaultInstallListener.java:31)
W/System.err: at freight.zengke.com.freight.widegt.appupdate._XUpdate.onInstallApk(_XUpdate.java:144)
W/System.err: at freight.zengke.com.freight.widegt.appupdate._XUpdate.startInstallApk(_XUpdate.java:126)
W/System.err: at freight.zengke.com.freight.widegt.appupdate.service.DownloadService$FileDownloadCallBack.onSuccess(DownloadService.java:336)
W/System.err: at freight.zengke.com.freight.widegt.appupdate.OKHttpUpdateHttpService$3.onResponse(OKHttpUpdateHttpService.java:162)
W/System.err: at freight.zengke.com.freight.widegt.appupdate.OKHttpUpdateHttpService$3.onResponse(OKHttpUpdateHttpService.java:149)
W/System.err: at com.zhy.http.okhttp.OkHttpUtils$5.run(OkHttpUtils.java:256)
W/System.err: at android.os.Handler.handleCallback(Handler.java:808)
W/System.err: at android.os.Handler.dispatchMessage(Handler.java:101)
W/System.err: at android.os.Looper.loop(Looper.java:166)
W/System.err: at android.app.ActivityThread.main(ActivityThread.java:7529)
W/System.err: at java.lang.reflect.Method.invoke(Native Method)
W/System.err: at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
W/System.err: Caused by: java.io.IOException: error=13, Permission denied
W/System.err: at java.lang.UNIXProcess.forkAndExec(Native Method)
W/System.err: at java.lang.UNIXProcess.(UNIXProcess.java:133)
W/System.err: at java.lang.ProcessImpl.start(ProcessImpl.java:128)
W/System.err: at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
W/System.err: ... 22 more

android 5.0 安装问题

android 5.0 使用默认安装弹出root请求(设置为false),给予root权限后面就没有了,没有进行安装

java.lang.VerifyError,请问jar是否有错误?

com.github.xuexiangjys:XUpdate:1.0.8

java.lang.VerifyError: com/xuexiang/xupdate/utils/UpdateUtils
at com.xuexiang.xupdate.UpdateManager$Builder.build(UpdateManager.java:642)
at com.xuexiang.xupdate.UpdateManager$Builder.update(UpdateManager.java:665)

老哥,能暴露一个改按钮颜色的属性吗

private void setDialogTheme(int color, int topResId) {
mIvTop.setImageResource(topResId);
mBtnUpdate.setBackgroundDrawable(DrawableUtils.getDrawable(UpdateUtils.dip2px(4, getContext()), color));
mNumberProgressBar.setProgressTextColor(color);
mNumberProgressBar.setReachedBarColor(color);
//随背景颜色变化
mBtnUpdate.setTextColor(ColorUtils.isColorDark(color) ? Color.WHITE : Color.BLACK);
}

这一段,,我的项目是只用白色的。。

功能建议

建议添加一种模式为,自动点击更新按钮。比静默模式更为友好
用户无需点击更新按钮,且可以直观看到更新进度

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.