1) Introduction to xUtils 3
xUtils is currently a relatively functional one Android Open source framework, recently released xUtil 3.0, improves the performance of the framework while adding new functionality. Here's a look at the official( https://github.com/wyouflf/xUtils3 ) Introduction to xUtils 3:
- xUtils includes many practical android tools.
- xUtils supports large file upload (more than 2G), more comprehensive http request protocol support (11 predicates), more flexible ORM, more event annotation support and not affected by confusion;
- xUtils is least compatible with Android 4.0 (api level 14);
- XUtils 3 has changed a lot, so a new project has been built that will not be maintained on the old version (github.com/wyouflf/xUtils), as opposed to the old version:
- HTTP implements replacing HttpClient as UrlConnection, automatically parsing callback generics, and safer breakpoint continuation strategy.
- Support standard Cookie policy, distinguish domain, path;
- Event annotation eliminates uncommon functions and improves performance.
- The database api simplifies and improves the performance, and achieves the same performance as greenDao.
- Image binding supports gif (some GIF files can only be displayed statically due to system compatibility), webp; supports corner, circle, square clipping, and supports automatic rotation.
2) Quickly configure xUtils 3 in our project
The configuration of xUtils 3 is very simple:
2-1) Add a dependency when using Gradle build
compile 'org.xutils:xutils:3.3.36'
- 1
- 1
If you use eclipse, you can click on the link below to download the aar file, then use zip to extract the jar package and so file.
Github Download: https://github.com/wyouflf/xUtils3
JCenter Download: http://jcenter.bintray.com/org/xutils/xutils/
Maven Download 1: http://central.maven.org/maven2/org/xutils/xutils/
Maven Download 2: http://repo1.maven.org/maven2/org/xutils/xutils/
2-2) Access permissions
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- 1
- 2
- 1
- 2
2-3) Create Application
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
x.Ext.init(this);
x.Ext.setDebug(false); //Output debug log, open will affect performance
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
2-4) Register MyApp in the Android Manifest file
<application
android:name=".MyApp"
...
</application>
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
XUtils 3 mainly includes annotation module, network module, picture module and data base Modules, the following will be explained one by one.
2. Use of xUtils 3 Annotation Module
The use of xUtils 3 annotation module in practical development is as follows:
1) Activity annotations are used as follows:
@ContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
@ViewInject(R.id.viewpager)
ViewPager viewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
x.view().inject(this);
...
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
2) The use of Fragment's annotations is as follows:
@ContentView(R.layout.fragment_http)
public class HttpFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return x.view().inject(this, inflater, container);
}
@Override
public void onViewCreated(View v, @Nullable Bundle savedInstanceState) {
super.onViewCreated(v, savedInstanceState);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
3) Set click events for buttons
- The method must be privately defined.
- The form of method parameters must be consistent with the Listener interface corresponding to type.
- Annotation parameter value supports arrays: value={id1, id2, id3}
/**
* Click events
* type Default View.OnClickListener.class, so you can simplify not writing, @Event(R.id.bt_main)
*/
@Event(type = View.OnClickListener.class,value = R.id.bt_main)
private void testInjectOnClick(View v){
Snackbar.make(v,"OnClickListener",Snackbar.LENGTH_SHORT).show();
}
/**
* Long press event
*/
@Event(type = View.OnLongClickListener.class,value = R.id.bt_main)
private boolean testOnLongClickListener(View v){
Snackbar.make(v,"testOnLongClickListener",Snackbar.LENGTH_SHORT).show();
return true;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
Emphasis: When using annotation module, attention must be paid to initializing view annotation framework.
3. Use of xUtils 3 Network Module
The xUtils 3 network module greatly facilitates the development of network module in actual development. The xUtils 3 network module roughly includes GET request, POST request, how to use other requests, upload files, download files, use caching and other functions. The following will explain one by one:
1) GET request
RequestParams params = new RequestParams(url);
params.addQueryStringParameter("username","abc");
params.addQueryStringParameter("password","123");
x.http().get(params, new Callback.CommonCallback<String>() {
@Override
public void onSuccess(String result) {
//Parsing result
}
//Callback method after request exception
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
//Initiative callback method to cancel request
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
Now let's look at GET requests with caches, POST requests are similar to other requests, and we won't go into that later.
GET requests with caches:
RequestParams params = new RequestParams(url);
params.addQueryStringParameter("username","abc");
params.addQueryStringParameter("password","123");
// Default cache lifetime in milliseconds (refer to if the server does not return valid max-age or Expires)
params.setCacheMaxAge(1000 * 60);
x.http().get(params, new Callback.CacheCallback<String>() {
private boolean hasError = false;
private String result = null;
@Override
public boolean onCache(String result) { //Get the cached data, the cache will not enter after expiration
this.result = result;
return true; //Trust cached data, no longer initiate network requests; false does not trust cached data
}
@Override
public void onSuccess(String result) {
//If the service returns 304 or onCache chooses the trust cache, then result is null
Log.i("JAVA", "Start asking");
if (result != null) {
this.result = result;
}
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
hasError = true;
Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();
if (ex instanceof HttpException) { //network error
HttpException httpEx = (HttpException) ex;
int responseCode = httpEx.getCode();
String responseMsg = httpEx.getMessage();
String errorResult = httpEx.getResult();
//...
} else { //Other mistakes
//...
}
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
if (!hasError && result != null) {
//Successful data acquisition
Toast.makeText(x.app(), result, Toast.LENGTH_LONG).show();
}
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
Some points to be noted in the onCache method above are as follows:
(a) If the server does not return the expiration time, refer to the params.setCacheMaxAge(maxAge) method.
b) The client will determine whether the local cache is given to the onCache method based on max-age or expires in the header returned by the server. If the server does not return max-age or expires, the cache will be kept until it is defined to return false, and xUtils will request new data to overwrite it.
c) If the cache is trusted to return true, the network will no longer be requested. Return false to continue requesting the network, but add ETag,Last-Modified and other information to the request header. If the server returns 304, it indicates that the data is not updated and does not continue to load the data.
2) POST request
RequestParams params = new RequestParams(url);
params.addBodyParameter("username","abc");
params.addParameter("password","123");
params.addHeader("head","android"); //Add a header for the current request
x.http().post(params, new Callback.CommonCallback<String>() {
@Override
public void onSuccess(String result) {
//Parsing result
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
3) Other network requests
RequestParams params = new RequestParams(url);
params.addParameter("username","abc");
x.http().request(HttpMethod.PUT, params, new Callback.CommonCallback<String>() {
@Override
public void onSuccess(String result) {
//Parsing result
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
4) Upload files
String path="/mnt/sdcard/Download/icon.jpg";
RequestParams params = new RequestParams(url);
params.setMultipart(true);
params.addBodyParameter("file",new File(path));
x.http().post(params, new Callback.CommonCallback<String>() {
@Override
public void onSuccess(String result) {
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
5) Download files
Here, the download of apk is illustrated as an example. After the download of apk is completed, the installation method of the system is automatically invoked.
url = "http://127.0.0.1/server/abc.apk";
RequestParams params = new RequestParams(url);
//Custom save path, Environment. getExternal Storage Directory (): Root directory of SD card
params.setSaveFilePath(Environment.getExternalStorageDirectory()+"/myapp/");
//Automatic Naming of Files
params.setAutoRename(true);
x.http().post(params, new Callback.ProgressCallback<File>() {
@Override
public void onSuccess(File result) {
//After downloading the apk, call the installation method of the system
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(result), "application/vnd.android.package-archive");
getActivity().startActivity(intent);
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
//Callback before network request
@Override
public void onWaiting() {
}
//Callback at the beginning of a network request
@Override
public void onStarted() {
}
//Method of constantly callback when downloading
@Override
public void onLoading(long total, long current, boolean isDownloading) {
//Current progress and total file size
Log.i("JAVA","current: "+ current +",total: "+total);
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
4.xUtils 3 Picture Module
The xUtils 3 image module focuses on the four bind methods of loading images, the use of loadDrable and loadFIle and the use of Image Options, which need more practice.
1)xUtils3 ImageOptions:
//Set the properties of the image through the ImageOptions.Builder().set method
ImageOptions imageOptions= new ImageOptions.Builder().setFadeIn(true).build(); //fadein
//Some other attributes of ImageOptions.Builder():
.setCircular(true) //Set the picture to be circular
.setSquare(true) //Set Pictures to Display Squares
.setCrop(true).setSize(200,200) //Set size
.setAnimation(animation) //Set animation
.setFailureDrawable(Drawable failureDrawable) //Setting the animation that failed to load
.setFailureDrawableId(int failureDrawable) //Setting the failed animation with the resource id
.setLoadingDrawable(Drawable loadingDrawable) //Setting the animation in loading
.setLoadingDrawableId(int loadingDrawable) //Setting the animation in loading with resource id
.setIgnoreGif(false) //Ignore Gif pictures
.setParamsBuilder(ParamsBuilder paramsBuilder) //Add some parameters to the network request
.setRaduis(int raduis) //Setting the corner radian
.setUseMemCache(true) //Set to use MemCache, default true
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
2) xUtils 3 bind method:
// assets file
x.image().bind(imageView, "assets://test.gif", imageOptions);
// local file
x.image().bind(imageView, new File("/sdcard/test.gif").toURI().toString(), imageOptions);
x.image().bind(imageView, "/sdcard/test.gif", imageOptions);
x.image().bind(imageView, "file:///sdcard/test.gif", imageOptions);
x.image().bind(imageView, "file:/sdcard/test.gif", imageOptions);
x.image().bind(imageView, url, imageOptions, new Callback.CommonCallback<Drawable>() {
@Override
public void onSuccess(Drawable result) {
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
3) xUtils 3 load Drawable method:
x.image().loadDrawable(url, imageOptions, new Callback.CommonCallback<Drawable>() {
@Override
public void onSuccess(Drawable result) {
imageView.setImageDrawable(result);
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
4) xUtils 3 loadFile method:
When we load an image through bind() or loadDrawable(), it will be saved to the local file, so when I need this image, I can search through the loadFile() method.
x.image().loadFile(url,imageOptions,new Callback.CacheCallback<File>(){
@Override
public boolean onCache(File result) {
//Here you can save pictures as wait operations.
Log.i("JAVA","file: "+result.getPath()+result.getName());
return true; //Trust that the local cache returns true
}
@Override
public void onSuccess(File result) {
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
4. Use of xUtils 3 database module
1) Initialize configuration and create entity classes
First, the DaoConfig configuration is initialized in the project Application (in the same level directory as the onCreate method):
/**
* Initialize the DaoConfig configuration
*/
DbManager.DaoConfig daoConfig = new DbManager.DaoConfig()
//Set the database name by default xutils.db
.setDbName("myapp.db")
//Set the database path and store it in the private directory of app by default
.setDbDir(new File("/mnt/sdcard/"))
//Setting the version number of the database
.setDbVersion(2)
//Setting up listeners for database opening
.setDbOpenListener(new DbManager.DbOpenListener() {
@Override
public void onDbOpened(DbManager db) {
//Opening database supports multi-threaded operation, improves performance, and greatly improves write speed.
db.getDatabase().enableWriteAheadLogging();
}
})
//Setting up monitoring of database updates
.setDbUpgradeListener(new DbManager.DbUpgradeListener() {
@Override
public void onUpgrade(DbManager db, int oldVersion, int newVersion) {
}
})
//Set up listeners for table creation
.setTableCreateListener(new DbManager.TableCreateListener() {
@Override
public void onTableCreated(DbManager db, TableEntity<?> table){
Log.i("JAVA", "onTableCreated: " + table.getName());
}
});
//Set whether transactions are allowed, default true
//.setAllowTransaction(true)
DbManager db = x.getDb(daoConfig);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
Then create the entity class of the database table ChildInfo:
/**
* onCreated = "sql": Write sql statements here when the first table creation requires insertion of data
*/
@Table(name = "child_info",onCreated = "")
public class ChildInfo {
/**
* name = "id": A field in a database table
* isId = true: Is it the primary key?
* autoGen = true: Is it automatic growth?
* property = "NOT NULL": Add constraint
*/
@Column(name = "id",isId = true,autoGen = true,property = "NOT NULL")
private int id;
@Column(name = "c_name")
private String cName;
public ChildInfo(String cName) {
this.cName = cName;
}
//The default constructor must be written, and if not, the table is not created successfully.
public ChildInfo() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getcName() {
return cName;
}
public void setcName(String cName) {
this.cName = cName;
}
@Override
public String toString() {
return "ChildInfo{"+"id="+id+",cName='"+cName+'\''+'}';
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
Then you can create and delete the database:
2) Create a database
//Insert multiple data into the child_info table with a set
ArrayList<ChildInfo> childInfos = new ArrayList<>();
childInfos.add(new ChildInfo("zhangsan"));
childInfos.add(new ChildInfo("lisi"));
childInfos.add(new ChildInfo("wangwu"));
childInfos.add(new ChildInfo("zhaoliu"));
childInfos.add(new ChildInfo("qianqi"));
childInfos.add(new ChildInfo("sunba"));
//The db.save() method can insert not only a single object, but also a collection.
db.save(childInfos);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
3) Delete the database
db.dropDb();
- 1
- 1
4) Delete tables
db.dropTable(ChildInfo.class);
- 1
- 1
5) Data in new tables
ChildInfo childInfo = new ChildInfo("zhangsan123");
db.save(childInfo);
- 1
- 2
- 1
- 2
6) Delete data from tables
//The first way of writing is:
db.delete(ChildInfo.class); //All data in child_info table will be deleted
//In the second way, deletion conditions are added:
WhereBuilder b = WhereBuilder.b();
b.and("id",">",2); //Constructive Modification Conditions
b.and("id","<",4);
db.delete(ChildInfo.class, b);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
7) Modify the data in the table
//The first way of writing is:
ChildInfo first = db.findFirst(ChildInfo.class);
first.setcName("zhansan2");
db.update(first,"c_name"); //c_name: The field name in the table
//The second way of writing:
WhereBuilder b = WhereBuilder.b();
b.and("id","=",first.getId()); //Constructive Modification Conditions
KeyValue name = new KeyValue("c_name","zhansan3");
db.update(ChildInfo.class,b,name);
//The third way of writing:
first.setcName("zhansan4");
db.saveOrUpdate(first);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
8) Data in Query Table
//Query the first data in the database table
ChildInfo first = db.findFirst(ChildInfo.class);
Log.i("JAVA",first.toString());
//Add query conditions to query
List<ChildInfo> all = db.selector(ChildInfo.class).where("id",">",2).and("id","<",4).findAll();
for(ChildInfo childInfo :all){
Log.i("JAVA",childInfo.toString());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
5.xUtils 3 provides some other methods
1) UI asynchronous execution
x.task().run(new Runnable() {
@Override
public void run() {
//Asynchronous code
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
2) UI synchronization
x.task().post(new Runnable() {
@Override
public void run() {
//Synchronization code
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
Reference article: https://github.com/wyouflf/xUtils3