Android performance optimization: Deciphering ZAKER, content caching loading methods for news applications such as Netease cloud reading, and androidzaker

Source: Internet
Author: User

Android performance optimization: Deciphering ZAKER, content caching loading methods for news applications such as Netease cloud reading, and androidzaker

I pay more attention to current affairs. Every day, I spend some time watching news or something. therefore, information aggregation applications like ZAKER and Netease cloud reading are my favorites, and these applications are indeed doing well and worth learning! In the previous article, I talked about some ideas about caching. I have written some caching practices for the LRUCache class before, but it is only stored in the application cache and is not suitable for long-term caching. this is a complete example of how to load a piece of news and cache the news for ZAKER applications.


As I wrote before, this article is only one of the implementation methods. It is only an elaboration of ideas and does not represent the best practice.


Effect

(If the image cannot be opened, copy the image address to the address bar. The source code is at the bottom of the Article .)



First, let's talk about the effect. In actual use, when a user opens a news item for the first time, the text is displayed, and the picture loaded by default is displayed. The user slides the screen slowly to view the news, at the same time, the pictures in the news are slowly loaded to make them more user-friendly, and even display the percentage or progress information of the pictures in the news, let the user know that the image is loaded as much as possible. then exit the current page and click another title to continue reading other news... repeat this operation.


For some reason, the network is closed, but the user is bored. On the bus, he wants to see the news he has browsed before, but the mobile phone network is closed for saving traffic and there is no WIFI on the bus. the user opens the application, clicks on the news title that he has read before, and can still see the complete news and all the pictures and texts.


At this point, we should understand that all the news text and images you have read are cached locally. the previous article has been written. images are generally suitable for caching files locally, and large texts are suitable for storing in databases. that's right ~


Next we hope to clarify the specific ideas, so we can implement it ourselves or see the following sample code for better understanding.


Train of Thought 1: How to Implement

ZAKER's news display uses WebView as the carrier, and then uses a custom HTML template to fill the local HTML template with the data transmitted from the server to the client for display. nice-looking HTML templates, including many general CSS and JS, are a product of excellent front-end developers. As mobile development, we do not need to know much about this. if necessary, the company's front-end developers can provide corresponding templates. therefore, the following example is just a simple HTML template to illustrate the principles.


1) After the server obtains the data of a news item used to populate the HTML template, we should also obtain the image information contained in this news item, such as the title word, image id, download Links of images and so on... store text content in the database.


2) then use some methods (usually JavaScript) to control the display of text on the WebView page, and the default picture in the loading. At the same time, download these images in a new local thread.


3) each time an image is downloaded, it is saved to a local bucket. and put the image information in the database or other places. in this way, we will use this image and directly use the download link of the image as the query condition. We will first go to the database to find the image. If so, we will get the local storage path information corresponding to the download link of the image. if the path is valid, it is used. If the path is invalid or the search fails, download it again.


4) the download of an image is not over yet. At last, the default image is replaced with the downloaded image in WebView.


2. A little bit of knowledge is required-JavaScript

After reading the above four steps, I believe that it is not difficult to obtain data from the server, access data to the database SQLite, and load the HTML source code in WebView. however, in some cases, the local code is difficult to complete, and JavaScript is needed. webView supports JavaScript, because it can complete a lot of page work, such as listening to web page rolling coordinates of WebView, controlling Page scrolling, and other common scenarios, similar to lazyload developed by web pages (delayed loading of images, asynchronous loading of images, etc.), the effects are implemented using js. in addition, JavaScript can interact with local code and be called, making WebView exceptionally powerful. for example, the above image is asynchronously loaded (the default image is displayed first, and then the image is downloaded to replace the default image), the image information is obtained on the page, and the local code is called to download the image, these are the key points of implementation, and they rely on JavaScript.


However, you should note that JavaScript is prone to security vulnerabilities. If you are careful, it will become the entrance to the attack. Therefore, you are not advised to use it at will.


After talking about the above pile, it is not difficult to understand the functions we will implement next. But there are several points to explain here:


1. WebView can directly load local resources, such as SD card, assets, raw and other folders.


2. Use a database with multiple threads, such as SQLite. pay attention to concurrent synchronization and other issues. for non-high concurrency and few threads, you can use the keyword synchronized in the simplest way. otherwise, it is easy to throw an error.


3. Save and read images. Pay attention to the file size. in specific applications, the recommended solution is that the server has compressed and Optimized images based on the mobile end, so that the client can reduce the processing complexity.

Imagine that a pile of text in a limited screen does not occupy any space traffic, but it is meaningless to give a few images of the original size. a thumbnail is displayed in the content. If you click the image to pop up, or click to save the source image, the source image is downloaded separately, ensuring the interaction comfort and the traffic and performance are not wasted.


So it is easy to understand the code.


The simple XML of the two pages used first,

Activity_main.xml

<RelativeLayout xmlns: android = "http://schemas.android.com/apk/res/android" xmlns: tools = "http://schemas.android.com/tools" android: id = "@ + id/container" android: layout_width = "match_parent" android: layout_height = "match_parent" tools: context = "com. alextam. webviewdemo. mainActivity "tools: ignore =" MergeRootFrame "> <TextView android: layout_width =" match_parent "android: layout_height =" 50dp "android: background =" #88888888 "android: text = "test WebView cache" android: textColor = "# FFFFFFFF" android: textSize = "18sp" android: gravity = "center" android: layout_alignParentTop = "true"/> <Button android: id = "@ + id/btn_start" android: layout_width = "wrap_content" android: layout_height = "wrap_content" android: padding = "20dp" android: text = "open page" android: layout_centerInParent = "true"/> </RelativeLayout>


Webview_act_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    >    <WebView        android:id="@+id/wv_main"        android:layout_width="match_parent"        android:layout_height="match_parent"        /></RelativeLayout>

Entry Activity, MainActivity:

This class is very simple, that is, creating a local cache folder to store pictures in news.

/*** Created on 5/21/2015 * @ author Alex Tam **/public class MainActivity extends Activity {private Button btn_start; private String rootPath; public static final String SEPERATOR = "/"; @ Overrideprotected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. activity_main); init ();} private void init () {btn_start = (Button) findViewById (R. id. btn_start); CRES AteCacheFolder (); btn_start.setOnClickListener (new OnClickListener () {@ Overridepublic void onClick (View v) {if (createCacheFolder ()) // check whether the local cache folder {Intent goIntent = new Intent (MainActivity. this, WebViewActicvity. class); startActivity (goIntent) ;}}) ;}// create a local cache folder-store the image private boolean createCacheFolder () {if (Environment. MEDIA_MOUNTED.equals (Environment. getExternalStorageState () {rootPath = Environme Nt. getExternalStorageDirectory (). getAbsolutePath () + SEPERATOR + "WebViewDemo"; File cFile = new File (rootPath); if (! CFile. exists () {cFile. mkdir () ;}return true ;}else {t (this, "unable to create local folder, please insert sd card"); return false ;}} public static final void t (Context context, String c) {Toast. makeText (context, c, Toast. LENGTH_SHORT ). show ();}}

For the Activity with specific display effects, WebViewActicvity:

Public class WebViewActicvity extends Activity {private WebView wv_main; // MAP-stores the information of the image to be displayed. private ConcurrentHashMap <String, String> map = new ConcurrentHashMap <String, String> (); // image folder private String rootPath = Environment. getExternalStorageDirectory (). getAbsolutePath () + MainActivity. SEPERATOR + "WebViewDemo"; private DAOHelper helper; // private List for storing image Downloaders <String> taskArray = new ArrayList <String> (); @ Overrideprotected void onCreate (Bundle savedInstanceState) {super. onCreate (savedInstanceState); setContentView (R. layout. webview_act_main); // database operation class helper = new DAOHelper (WebViewActicvity. this); start ();} private void start () {wv_main = (WebView) findViewById (R. id. wv_main); wv_main.getSettings (). setJavaScriptEnabled (true); wv_main.setWebViewClient (new WebViewClient (); // wv_main.getSettings () is displayed in a single column (). setLa YoutAlgorithm (LayoutAlgorithm. SINGLE_COLUMN); // Add the JavaScript interface // the name "mylistner" cannot be written incorrectly. // because the JavaScript method in the HTML loaded by WebView calls back the method in mylistner, therefore, the two names must be consistent: wv_main.addJavascriptInterface (new JavascriptInterface (webviewacticinterface. this), "mylistner"); // to simulate requests for data to the server and load HTML, I have prepared a copy in advance and loaded wv_main.loadUrl ("file: /// android_asset/wv_content.html ");} private class JavascriptInterface {private Context context; pub Lic JavascriptInterface (Context context) {this. context = context;} // this method is called back to replace the default image on the page @ android. webkit. javascriptInterfacepublic String replaceimg (String imgPosition, String imgUrl, String imgTagId) {if (! Map. containsKey (imgUrl) {// if the image information exists in the mediation memory MAP, it is directly used and no longer used to query String imgPath = helper. find (imgUrl); if (imgPath! = Null & new File (imgPath ). exists () {map. put (imgUrl, imgPath); return imgPath;} else {if (taskArray. indexOf (imgUrl) <0) {// when the image link does not exist in the database and the task of downloading the link is not found, after a new download task is added, taskArray is automatically replaced. add (imgUrl); DownLoadTask task = new DownLoadTask (imgTagId, imgPosition, imgurl1_1_task.exe cute ();} // to simulate the loading progress of the default image, here, we return another different default image. // you can change this image to return "file" in a certain percentage as needed: /// android_asset/test.jpg ";}} else {return m Ap. get (imgUrl) ;}}// private class DownLoadTask extends AsyncTask <Void, Void, String> {String imageId; // tag idString imagePosition; // mark String imgUrl as the position of the Image array; // public DownLoadTask (String imageId, String imagePosition, String imgUrl) {this. imageId = imageId; this. imagePosition = imagePosition; this. imgUrl = imgUrl;} @ Overrideprotected String doInBackground (Void... params) {try {// download image URL = ne W URL (imgUrl); HttpURLConnection conn = (HttpURLConnection) url. openConnection (); conn. setConnectTimeout (20*1000); conn. setReadTimeout (20*1000); conn. setRequestMethod ("GET"); conn. connect (); InputStream in = conn. getInputStream (); byte [] myByte = readStream (in); // Compressed Storage. You can put bitmap into another cache and use it for other purposes, for example, click the image to enlarge Bitmap bitmap = BitmapFactory. decodeByteArray (myByte, 0, myByte. length); String fileName = Long. toString (System. currentTimeMillis () + ". jpg "; File imgFile = new File (rootPath + MainActivity. SEPERATOR + fileName); BufferedOutputStream bos = new BufferedOutputStream (new FileOutputStream (imgFile); bitmap. compress (Bitmap. compressFormat. JPEG, 80, bos); bos. flush (); bos. close (); return imgFile. getAbsolutePath ();} catch (Exception e) {e. printStackTrace ();} return null;} @ Overrideprotected void OnPostExecute (String imgPath) {super. onPostExecute (imgPath); if (imgPath! = Null) {// call the js method on the page to replace the default image with the downloaded image String url = "javascript :( function () {" + "var img = document. getElementById (\ "" + imageId + "\"); "+" if (img! = Null) {"+" img. src = \ "" + imgPath + "\";} "+"}) () "; wv_main.loadUrl (url); // cache the image information into the mediation memory map. put (imgUrl, imgPath); // cache the image information to the database helper. save (imgUrl, imgPath);} else {Log. e ("WebViewActicvity error", "DownLoadTask has a invalid imgPath... ") ;}} private byte [] readStream (InputStream inStream) throws Exception {ByteArrayOutputStream outStream = new ByteArrayOutputStream (); byte [] buffer = new byte [2048]; Int len = 0; while (len = inStream. read (buffer ))! =-1) {outStream. write (buffer, 0, len);} outStream. close (); inStream. close (); return outStream. toByteArray ();}}


The image in the assets folder can be replaced with your own image. for the key HTML template content, I would like to thank a front-end developer for his modifications. this is his original JavaScript to implement Page scrolling image loading. We recommend that you first read the original text and understand the implementation principles. If you do not know anything, please search or read a book. the DOM method used in it is very useful and worth further study!


The following is the modified HTML source code. wv_content.html is placed in the assets Folder:

(News text and images in HTML are from the favorite ifanr-love fan ER network. go to Starbucks and have a cup of coffee with the technologyOne of the frequently-watched technology news networks, thank you! )

<! DOCTYPE html PUBLIC "-// W3C // dtd xhtml 1.0 Transitional // EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 


In this example, when you enter the WebView page, the text and default image will be displayed when loading HTML for the first time, and then another loaded image (such as indicating the progress) will be displayed ), at the same time, you need to download images from the network. When the image is downloaded, the old image will be replaced no matter the page is rolling or not rolling. at this time, the image information will be stored in both the database and the intermediary storage MAP. If the MAP has image information, it will not be searched in the database (the data that can be obtained in the application should not be read from the database as far as possible .) when the page is closed, the source image can be quickly displayed even if the network is closed. this is the overall effect.


In addition to local code, the JavaScript code involved in HTML is also the key to implementation. this js checks the img tag information every time the page is rolled, so that each scrolling has the opportunity to check the image loading progress. at the same time, place the js interface for image replacement locally to load and call. by combining the two, you can increase the percentage of loading progress for an image. Of course, each percentage actually corresponds to its own image. You only need to return an image representing a certain percentage at different progresses. in this example, the cache implementation focuses on images and explains how to simulate the ZAKER application to cache news. Therefore, how to save the text in the database is not implemented, I believe everyone can do it by themselves. we hope this example will help us better understand the loading methods of news application content and make better applications.


That's it. Good night.


Source code download

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.