This project was actually written last year, but at that time it did not use a base class. Instead, it used the NetEase Youdao Translation SDK, and the coupling aspect was not done very well. During the winter vacation, I refactored it and used RxJava2 and Retrofit for network requests, as well as ButterKnife. I consider this project as my own learning experience, and I will gradually update it with any areas that can be optimized in the future.
Project link: starTranslation
Partial Knowledge Points#
Use of Retrofit and RxJava2#
1. Create Service class
Since RxJava is used, the return type is no longer a Call, but an Observable.
public interface networkApi {
@GET("api?")
Observable<TranslationBean> translateYouDao(
@Query("q") String q,
@Query("from") String from,
@Query("to") String to,
@Query("appKey") String appKey, // Application ID
@Query("salt") String salt, // UUID
@Query("sign") String sign, // Application ID+input+salt+curtime+Application Secret. input= first 10 characters of q+length of q+last 10 characters of q (length of q >= 20) or input = string
@Query("signType") String signType, // Signature type
@Query("curtime") String curtime // Timestamp
);
}
2. Create the request process
public class netWork {
private static networkApi sContactsApi;
private static OkHttpClient okHttpClient = new OkHttpClient();
private static Converter.Factory gsonConverterFactory = GsonConverterFactory.create();
private static CallAdapter.Factory rxJavaCallAdapterFactory = RxJava2CallAdapterFactory.create();
private static class ApiClientHolder {
public static final netWork INSTANCE = new netWork();
}
public static netWork getInstance() {
return ApiClientHolder.INSTANCE;
}
public networkApi getDataService() {
if (sContactsApi == null) {
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(Constants.BASE_URL)
.addConverterFactory(gsonConverterFactory)
.addCallAdapterFactory(rxJavaCallAdapterFactory)
.build();
sContactsApi = retrofit.create(networkApi.class);
}
return sContactsApi;
}
}
3. Send requests and process data
@SuppressLint("CheckResult")
public void netConnection(String q,String from,String to,String salt,String sign,String curtime){
netWork.getInstance().getDataService()
.translateYouDao(q,from,to,appID,salt,sign,signType,curtime)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<TranslationBean>() {
@Override
public void accept(TranslationBean translationBean) throws Exception {
List<TranslationBean> list_word = new ArrayList<>();
list_word.add(translationBean);
mView.showResult(list_word);
}
});
}
4. Extensions
The above code is learned from the source code of a senior student. The encapsulation is not very good. When searching for other usage methods, I found someone else's encapsulation method. If I have time, I need to review it.
Here is the other person's method: Android Elegant Use of RxJava2.0+Retrofit2.0
About Toolbar#
Toolbar is a powerful control, and basically every Activity needs it. Previously, I used to write a toolbar for each layout and initialize the view with ButterKnife, but this approach was very cumbersome... So I encapsulated the toolbar in BaseActivity. As for the layout, I first wrote a toolbar layout according to my own needs, and then included it where needed (I also wrote the button for switching ViewPager pages in this way).
Specific usage
First, write a toolbar layout
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="##1B6FB3"
android:layout_alignParentTop="true"
android:id="@+id/mtoolbar"
android:layout_height="?attr/actionBarSize"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
</androidx.appcompat.widget.Toolbar>
Then include it where needed
<include
layout="@layout/view_toolbar"/>
The same method can also be used to write tab bars, etc.
Use of Room#
Since I had never upgraded the database when using Room before, this was the first time I encountered this issue. After modifying the table according to my poor English, I understood that it required me to upgrade the version. However, after upgrading the version, I encountered the following error:
java.lang.IllegalStateException: A migration from 1 to 2 is necessary. Please provide a Migration in the builder or call fallbackToDestructiveMigration in the builder in which case Room will re-create all of the tables.
What is this? I copied and pasted it to Google and found two solutions.
Increase the version and use fallback migration (data will be cleared)
private static wordDatabase buildDatabase(Context context) {
return Room.databaseBuilder(context.getApplicationContext(), wordDatabase.class, "StarWord.db")
.allowMainThreadQueries()
.fallbackToDestructiveMigration() // When upgrading the repository, it will be rebuilt and the data will be cleared
.build();
}
In this case, when Room starts, it checks if the version has increased. If it has, the contents of the database will be cleared and the table will be recreated.
Increase the version and provide Migration (data remains intact)
I did not use this method because I probably won't modify the database again, but I still want to learn it.
// Add a migration from version 1 to 2
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
@Override
public void migrate(SupportSQLiteDatabase database) {
// Here, write the table modification
//database.execSQL("ALTER TABLE Starword " + " ADD COLUMN test INTEGER"); // Add a column named "test" to the table
}
};
Then add this migration to the databaseBuilder
private static wordDatabase buildDatabase(Context context) {
return Room.databaseBuilder(context.getApplicationContext(), wordDatabase.class, "StarWord.db")
.allowMainThreadQueries()
.addMigrations(MIGRATION_1_2)
.build();
}
Now the database table has been updated, and the old data has been preserved.
Self-reflection#
- At the beginning, when clicking on a word in the favorites list, I was planning to make the viewpager jump back to the first page and perform a new search. However, I couldn't figure out how to jump using the view returned by the adapter, so I came up with an alternative method of displaying a dialog. I still haven't found a solution...
- Although there is a Retrofit data entity class, I still wrote a Room data class to store the data. If possible, I would like to write it as one class to reduce code duplication.
- Some details were not done well, and overall, the liveliness of the app is still lacking.
That's it for now. I will continue to add more if I encounter any more issues in the future.