享受Android应用程序的Java技术盛宴

Java™ 语言是 Android 开发人员所选的工具。Android 运行时使用自己的虚拟机Dalvik,这并不是多数程序开发人员使用的普通Java 虚拟机。Dalvik支持Java 编程语言的大部分功能——但并不是全部。在本文中,您将学习高级Java功能及其如何在Android中实现。这些功能包括并发性、联网和数据库访问。

准备开始

本文介绍几种处理复杂情况的 Android SDK 工具。开发 Android 应用程序,需要最新版 Android SDK,这需要一个 Java Development Kit (JDK)。我使用的是 Android 2.2 和 JDK 1.6.0_17(这些工具的链接见 参考资料)。物理设备不是必须的;本文中的所有代码在 SDK 附带的 Android 模拟器上可以很好的运行。本文假设您对 Android 编程比较熟悉,故不涉及 Android 基础开发,但是如果您有 Java 编程语言的知识,也是可以的。

并发性和联网

Android 应用程序一个最常见的任务就是检索数据或通过网络将数据发送到远程服务器。这一操作的结果通常是一些您想要展示给用户的新数据。这意味着您需要修改用户界面。大多数开发人员知道您将不会执行一个潜在的长期运行任务,例如,在主 UI 线程上通过网络访问数据(特别使用一个网络连接非常慢的手机)。冻结您的应用程序直至长期运行任务完成。事实上,如果这个任务超过 5 秒,Android 操作系统将出现臭名昭著的 Application Not Responding 对话框,如图1所示。

图 1. Android 臭名昭著的 Application Not Responding 对话框

您不可能知道用户网络连接能有多慢。为了避免冒险,您必须在不同的线程上执行任务,或者至少不在主 UI 线程上执行。许多 Android 应用程序,但不是全部,需要处理多线程,由此引起并发。应用程序经常需要本地保存数据,Android 数据库是一个很好的选择。这三个场景(不同线程,并发和本地保存数据)在 Java 环境中有许多标准 方法可以用来处理。然而,正如您将要看到的,Android 提供不同的选择。让我们逐个看看,看看其优点和缺点。

Android 网络

通过网络使用 Java 编程进行调用是简单的,我们熟悉的 java.net 包含几个执行此操作的类。这些类大多数在 Android 中都可用,事实上,您可以使用像 java.net.URL 和 java.net.URLConnection 这样的类,就像您在其他 Java 应用程序中那样。然而,Android 包括 pache HttpClient 库,这是在 Android 上连接网络的首选方法。即使您使用常用 Java 类,Android 实现仍然使用 HttpClient。清单 1 显示了一个使用这个必不可少的库的示例。(所有源代码见 下载。)

清单 1. 在 Android 上使用 Http Client 库

  1. private ArrayList<Stock> fetchStockData(Stock[] oldStocks)   
  2.     throws ClientProtocolException, IOException{  
  3.     StringBuilder sb = new StringBuilder();  
  4.     for (Stock stock : oldStocks){  
  5.         sb.append(stock.getSymbol());  
  6.         sb.append('+');  
  7.     }  
  8.     sb.deleteCharAt(sb.length() - 1);  
  9.     String urlStr =   
  10.         "http://finance.yahoo.com/d/quotes.csv?f=sb2n&s=" +   
  11.                 sb.toString();  
  12.     HttpClient client = new DefaultHttpClient();  
  13.     HttpGet request = new HttpGet(urlStr.toString());  
  14.     HttpResponse response = client.execute(request);  
  15.     BufferedReader reader = new BufferedReader(  
  16.             new InputStreamReader(response.getEntity().getContent()));  
  17.     String line = reader.readLine();  
  18.     int i = 0;  
  19.     ArrayList<Stock> newStocks = new ArrayList<Stock>(oldStocks.length);  
  20.     while (line != null){  
  21.         String[] values = line.split(",");  
  22.         Stock stock = new Stock(oldStocks[i], oldStocks[i].getId());  
  23.         stock.setCurrentPrice(Double.parseDouble(values[1]));  
  24.         stock.setName(values[2]);  
  25.         newStocks.add(stock);  
  26.         line = reader.readLine();  
  27.         i++;  
  28.     }  
  29.     return newStocks;  

在这段代码中有一组 Stock 对象。这是基本的数据结构对象,保存用户拥有股票信息(比如,代号、价格等)以及更多的个人信息(比如,用户付了多少钱)。您可以使用 HttpClient 类从 Yahoo Finance 检索动态数据(例如,这支股票目前的价格)。HttpClient 包含一个 HttpUriRequest ,在本例中,您可以使用 HttpGet,这是 HttpUriRequest 的一个子类。类似地,当您需要向远程服务器发送数据时,可以使用 HttpPost 类,当您从客户端得到 HttpResponse 时,您能接触到响应的潜在 InputStream、对其进行缓冲、解析来获取股票信息。

现在,您看到了如何通过网络检索数据、如何用这个数据来通过使用多线程智能地更新 Android UI。

Android 并发性实践

如果您在应用程序的主 UI 线程上运行 清单 1 中的代码,可能会出现 Application Not Responding 对话框,具体视用户网络速度而定。因此必须确定生成一个线程来获取数据。清单 2 显示了一种解决方法。

清单 2. Naïve 多线程(别这样,这行不通!)

  1. private void refreshStockData(){  
  2.     Runnable task = new Runnable(){  
  3.         public void run() {  
  4.             try {  
  5.                 ArrayList<Stock> newStocks =   
  6.                     fetchStockData(stocks.toArray(  
  7.                                   new Stock[stocks.size()]));  
  8.                 for (int i=0;i<stocks.size();i++){  
  9.                     Stock s = stocks.get(i);  
  10.                     s.setCurrentPrice(  
  11.                                   newStocks.get(i).getCurrentPrice());  
  12.                     s.setName(newStocks.get(i).getName());  
  13.                     refresh();  
  14.                 }  
  15.             } catch (Exception e) {  
  16.                 Log.e("StockPortfolioViewStocks",   
  17.                             "Exception getting stock data", e);  
  18.             }  
  19.         }  
  20.     };  
  21.     Thread t = new Thread(task);  
  22.     t.start();  

清单 2 的标题声明这是 naïve 代码,确实是。在这个例子中,您将调用 清单 1 中的 fetchStockData 方法,将其封装在 Runnable 对象中,并在一个新线程中执行。在这个新线程中,您可以访问 stocks,一个封装 Activity(此类创建了 UI)的成员变量。顾名思义,这是 Stock 对象的一个数据结构(本例中是 java.util.ArrayList)。换句话说,您在两个线程之间共享数据,主 UI 线程和衍生(spawned)线程(在 清单 2 中调用)。当您修改了衍生线程中的共享数据时,通过在 Activity 对象上调用 refresh 方法来更新 UI。

如果您编写了 Java Swing 应用程序,您可能需要遵循一个像这样的模式。然而,这在 Android 中将不能正常工作。衍生线程根本不能修改 UI。因此在不冻结 UI ,但另一方面,在数据收到之后又允许您修改 UI 的情况下,您怎样检索数据?android.os.Handler 类允许您在线程之间协调和通信。清单 3 显示了一个使用 Handler 的已更新 refreshStockData 方法。

清单 3. 实际工作的多线程 — 通过使用 Handler

  1. private void refreshStockData(){  
  2.     final ArrayList<Stock> localStocks =   
  3.           new ArrayList<Stock>(stocks.size());  
  4.     for (Stock stock : stocks){  
  5.         localStocks.add(new Stock(stock, stock.getId()));  
  6.     }  
  7.     final Handler handler = new Handler(){  
  8.         @Override 
  9.         public void handleMessage(Message msg) {  
  10.             for (int i=0;i<stocks.size();i++){  
  11.                 stocks.set(i, localStocks.get(i));  
  12.             }  
  13.             refresh();  
  14.         }  
  15.     };  
  16.     Runnable task = new Runnable(){  
  17.         public void run() {  
  18.             try {  
  19.                 ArrayList<Stock> newStocks =   
  20.                     fetchStockData(localStocks.toArray(  
  21.                                   new Stock[localStocks.size()]));  
  22.                 for (int i=0;i<localStocks.size();i++){  
  23.                     Stock ns = newStocks.get(i);  
  24.                     Stock ls = localStocks.get(i);  
  25.                     ls.setName(ns.getName());  
  26.                     ls.setCurrentPrice(ns.getCurrentPrice());  
  27.                 }  
  28.                 handler.sendEmptyMessage(RESULT_OK);  
  29.             } catch (Exception e) {  
  30.                 Log.e("StockPortfolioViewStocks",   
  31.                             "Exception getting stock data", e);  
  32.             }   
  33.         }  
  34.     };  
  35.     Thread dataThread = new Thread(task);  
  36.     dataThread.start();  

在 清单 2 和 清单 3 中的代码有两个主要的不同。明显的差异是 Handler 的存在。第二个不同是,在衍生线程中,您不能修改 UI。相反的,当您将消息发送到 Handler,然后由 Handler 来修改 UI。也要注意,在线程中您不能修改 stocks 成员变量,正如您之前所做的。相反地您可以修改数据的本地副本。严格地来说,这是不是必须的,但这更为安全。

清单 3 说明了在并发编程中一些非常普遍的模式:复制数据、将数据解析到执行长期任务的线程中、将结果数据传递回主 UI 线程、以及根据所属数据更新主 UI 线程。Handlers 是 Android 中的主要通信机制,它们使这个模式易于实现。然而,清单 3 中仍然有一些样本代码。幸好,Android 提供方法来封装和消除大多数样本代码。清单 4 演示了这一过程。

清单 4. 用一个 AsyncTask 使多线程更容易

  1. private void refreshStockData() {  
  2.     new AsyncTask<Stock, Void, ArrayList<Stock>>(){  
  3.         @Override 
  4.         protected void onPostExecute(ArrayList<Stock> result) {  
  5.             ViewStocks.this.stocks = result;  
  6.             refresh();  
  7.         }  
  8.  
  9.         @Override 
  10.         protected ArrayList<Stock> doInBackground(Stock... stocks){  
  11.             try {  
  12.                 return fetchStockData(stocks);  
  13.             } catch (Exception e) {  
  14.                 Log.e("StockPortfolioViewStocks""Exception getting stock data", e);  
  15.             }  
  16.             return null;  
  17.         }  
  18.     }.execute(stocks.toArray(new Stock[stocks.size()]));  

如您所见,清单 4 比起 清单 3 样本代码明显减少。您不能创建任何线程或 Handlers。使用 AsyncTask 来封装所有样本代码。要创建 AsyncTask,您必须实现 doInBackground 方法。该方法总是在独立的线程中执行,因此您可以自由调用长期运行任务。它的输入类型来自您所创建的 AsyncTask 的类型参数。在本例中,第一个类型参数是 Stock,因此 doInBackground 获得传递给它的一组 Stock 对象。类似地,它返回一个 ArrayList<Stock>,因为这是 AsyncTask 的第三个类型参数。在此例中,我也选择重写 onPostExecute 方法。这是一个可选方法,如果您需要使用从 doInBackground 返回的数据来进行一些操作,您可以选用这种方法来实现。这个方法总是在主 UI 线程上被执行,因此对于修改 UI 这是一个很好的选择。

有了 AsyncTask,您就完全可以简化多线程代码。它可以将许多并发陷阱从您的开发路径删除,您仍然可以使用 AsyncTask 寻找一些潜在问题,例如,在 doInBackground 方法对象执行的同时设备上的方向发生改变时可能发生什么。更多关于如何处理这类案例的技术,见 参考资料 的链接。

现在我们开始讨论另一个常见任务,其中 Android 明显背离常用的 Java 方法 — 使用数据库进行处理。

Android 数据库连通性

Android 中一个非常有用的特征就是存在本地关系数据库。保证您能在本地文件中存储您的数据,但通常更有用的是使用一个关系型数据库管理系统(Relational Database Management System,RDBMS)来存储。Android 提供给您常用的 SQLite 数据库来进行处理,因为对于像 Android 这类嵌入式系统它是高度优化的。它被 Android 上的核心应用程序所用。例如,用户地址簿是存储在一个 SQLite 数据库中。现在,对于给定的 Android 的 Java 实现,您可以使用 JDBC 来访问这些数据库。出人意料的是,Android 甚至包括构成主要部分 JDBC API 的 java.sql 和 javax.sql 包。然而,当涉及使用本地 Android 数据库进行处理时,这毫无用处。相反地,您想要使用 android.database 和 android.database.sqlite 包。清单 5 是一个使用这些类存储和检索数据的示例。

清单 5. 使用 Android 进行数据库访问

  1. public class StocksDb {  
  2.     private static final String DB_NAME = "stocks.db";  
  3.     private static final int DB_VERSION = 1;  
  4.     private static final String TABLE_NAME = "stock";  
  5.     private static final String CREATE_TABLE = "CREATE TABLE " +   
  6.         TABLE_NAME + " (id INTEGER PRIMARY KEY, symbol TEXT, max_price DECIMAL(8,2), " +  
  7.             "min_price DECIMAL(8,2), price_paid DECIMAL(8,2), " +  
  8.             "quantity INTEGER)";  
  9.     private static final String INSERT_SQL = "INSERT INTO " + TABLE_NAME +  
  10.             " (symbol, max_price, min_price, price_paid, quantity) " +  
  11.             "VALUES (?,?,?,?,?)";  
  12.     private static final String READ_SQL = "SELECT id, symbol, max_price, " +  
  13.             "min_price, price_paid, quantity FROM " + TABLE_NAME;  
  14.     private final Context context;  
  15.     private final SQLiteOpenHelper helper;  
  16.     private final SQLiteStatement stmt;  
  17.     private final SQLiteDatabase db;  
  18.     public StocksDb(Context context){  
  19.         this.context = context;  
  20.         helper = new SQLiteOpenHelper(context, DB_NAME, null,   
  21.                 DB_VERSION){  
  22.             @Override 
  23.             public void onCreate(SQLiteDatabase db) {  
  24.                 db.execSQL(CREATE_TABLE);  
  25.             }  
  26.  
  27.             @Override 
  28.             public void onUpgrade(SQLiteDatabase db, int oldVersion,   
  29.                     int newVersion) {  
  30.                 throw new UnsupportedOperationException();  
  31.             }  
  32.         };  
  33.         db = helper.getWritableDatabase();  
  34.         stmt = db.compileStatement(INSERT_SQL);  
  35.     }  
  36.     public Stock addStock(Stock stock){  
  37.         stmt.bindString(1, stock.getSymbol());  
  38.         stmt.bindDouble(2, stock.getMaxPrice());  
  39.         stmt.bindDouble(3, stock.getMinPrice());  
  40.         stmt.bindDouble(4, stock.getPricePaid());  
  41.         stmt.bindLong(5, stock.getQuantity());  
  42.         int id = (int) stmt.executeInsert();  
  43.         return new Stock (stock, id);  
  44.     }  
  45.     public ArrayList<Stock> getStocks() {  
  46.         Cursor results = db.rawQuery(READ_SQL, null);  
  47.         ArrayList<Stock> stocks =   
  48.                  new ArrayList<Stock>(results.getCount());  
  49.         if (results.moveToFirst()){  
  50.             int idCol = results.getColumnIndex("id");  
  51.             int symbolCol = results.getColumnIndex("symbol");  
  52.             int maxCol = results.getColumnIndex("max_price");  
  53.             int minCol = results.getColumnIndex("min_price");  
  54.             int priceCol = results.getColumnIndex("price_paid");  
  55.             int quanitytCol = results.getColumnIndex("quantity");  
  56.             do {  
  57.                 Stock stock = new Stock(results.getString(symbolCol),   
  58.                         results.getDouble(priceCol),   
  59.                         results.getInt(quanitytCol),   
  60.                                     results.getInt(idCol));  
  61.                 stock.setMaxPrice(results.getDouble(maxCol));  
  62.                 stock.setMinPrice(results.getDouble(minCol));  
  63.                 stocks.add(stock);  
  64.             } while (results.moveToNext());  
  65.         }  
  66.         if (!results.isClosed()){  
  67.             results.close();  
  68.         }  
  69.         return stocks;  
  70.     }  
  71.     public void close(){  
  72.         helper.close();  
  73.     }      

清单 5 中的类完全封装了一个用于存储股票信息的 SQLite 数据库。因为您将要使用一个嵌入式数据库,不仅是您的应用程序要使用它,而且也要通过应用程序来创建它。您需要提供代码来创建该数据库。Android 提供一个有用的抽象帮助类 SQLiteOpenHelper。要完成这一操作,您需要扩展这个抽象类并提供代码通过使用 onCreate 方法创建您的数据库。当您有一个帮助程序实例时,就可以获取一个 SQLiteDatabase 实例,您可以用来执行任意 SQL 语句。

您的数据库类有两个较为方便的方法。第一个是 addStock,用于将新股票保存到数据库中。注意,您使用了一个 SQLiteStatement 实例,这类似于一个 java.sql.PreparedStatement。需要注意的是,在您的类构造器中如何对其进行编译,使其在每次调用 addStock 时都能重复利用。在每个 addStock 调用中,SQLiteStatement 的变量(INSERT_SQL 字符串中的问号)必然要将数据传递给 addStock。再一次强调,这类似于 PreparedStatement ,您可以从 JDBC 了解它。

另一个方法是 getStocks。顾名思义,它从数据库中检索所有股票。注意,您再次使用一个 SQL 字符串,正如您在 JDBC 中所用的那样。您可以在 SQLiteDatabase 类上通过使用 rawQuery 方法来进行处理。这个类也有几个查询方法,让您可以不使用 SQL 直接查询数据库。所有这些方法都返回一个 Cursor 对象,和 java.sql.ResultSet 非常相似。您可以将 Cursor 移动到从数据库中返回的数据所在行,在每一行,您可以使用 getInt、getString 和其他的方法来检索您要查询的数据库中各列相关的值。再一次强调,这和 ResultSet 十分相似。也和 ResultSet 比较相似,当您完成操作之后,关闭 Cursor 也十分重要的。如果您没有关闭 Cursors,那么可能会迅速地耗尽内存并导致您的应用程序崩溃。

查询本地数据库是一个比较慢的过程,特别是,如果您有多行数据或者您需要在多个表之间运行复杂的查询语句。然而,数据库查询或插入超过 5 秒且出现一个 Application Not Responding 对话框,这种情况不太可能发生,但是当您的数据库忙于读取和写入数据时,冻结您的 UI 是不明智的。当然,避免这种情况最好的办法是使用 AsyncTask。清单 6 展示了这个示例。

清单 6. 在一个单独的线程上插入数据库

  1. Button button = (Button) findViewById(R.id.btn);  
  2. button.setOnClickListener(new OnClickListener(){  
  3.     public void onClick(View v) {  
  4.         String symbol = symbolIn.getText().toString();  
  5.         symbolIn.setText("");  
  6.         double max = Double.parseDouble(maxIn.getText().toString());  
  7.         maxIn.setText("");  
  8.         double min = Double.parseDouble(minIn.getText().toString());  
  9.         minIn.setText("");  
  10.         double pricePaid =   
  11.                 Double.parseDouble(priceIn.getText().toString());  
  12.         priceIn.setText("");  
  13.         int quantity = Integer.parseInt(quantIn.getText().toString());  
  14.         quantIn.setText("");  
  15.         Stock stock = new Stock(symbol, pricePaid, quantity);  
  16.         stock.setMaxPrice(max);  
  17.         stock.setMinPrice(min);  
  18.         new AsyncTask<Stock,Void,Stock>(){  
  19.             @Override 
  20.             protected Stock doInBackground(Stock... newStocks) {  
  21.                 // There can be only one!  
  22.                 return db.addStock(newStocks[0]);  
  23.             }  
  24.             @Override 
  25.             protected void onPostExecute(Stock s){  
  26.                 addStockAndRefresh(s);  
  27.             }  
  28.         }.execute(stock);  
  29.     }  
  30. }); 

您可以先为按钮创建一个实践监听器。当用户点击按钮时,您可以从各个小部件(确切地说是 EditText 小部件)读取股票数据并填入一个新的 Stock 对象。您可以创建一个 AsyncTask,并通过 doInBackground 方法从 清单 5 中调用 addStock 方法。如此,addStock 将在一个背景线程上执行,而不是在主 UI 线程上。完成之后,将新 Stock 对象从数据库传递到在主 UI 线程上执行的 addStockAndRefresh 方法。

结束语

本文显示了,即使 Android 在 Java 环境下仅支持众多 API 的一个子集,但是其功能却一点都不逊色。在某些示例中,比如网络,它完全实现了熟悉的 API,也提供了一些更为便捷的方法。在其他的示例中,比如并发性,Android 添加了额外 API 以及一些必须遵循的惯例。最后,在数据库访问案例中,Android 提供了完全不同的方法来访问数据库,但使用的是熟悉的概念。这不仅仅是标准 Java 和 Android Java 技术之间主观上的差异:它们形成了Android 开发的基础构建元件。

作者简介:

Michael Galpin

Michael Galpin 是eBay 的一名架构师。他经常为 developerWorks 撰写文章,同时在 TheServerSide.com 和 Java Developer 期刊上发表文章,他还有自己的博客。他从 1998 年开始做职业程序员,并拥有加州理工学院数学专业的学士学位。

更多内容请见:http://www.ibm.com/developerworks/cn/xml/x-gourmetandroid/index.html

转载于:https://www.cnblogs.com/mixer/archive/2010/09/10/2448796.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/362585.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

echarts地图在ie浏览器上不显示

前面是安装及上手教程&#xff0c;参考了彩色泡泡 和 winne雪 的博客。 如果已经写好了&#xff0c;可以直接跳到 问题总结。 1. npm install echarts --save 2. 在 main.js 中 import echarts from echarts; Vue.prototype.$echarts echarts;3. 在页面上 import china fr…

虚拟主机上快速安装kail

官方文档的筛选 【1】Kali Linux是什么? Kali Linux是一个高级渗透测试和安全审计Linux发行版. Kali Linux 特性 Kali是BackTrack Linux完全遵循Debian开发标准的完整重建.全新的目录框架、复查并打包所有工具、还为VCS建立了Git 树. 超过300个渗透测试工具: 复查了BackTrack里…

Java并发教程–线程之间的可见性

当在不同线程之间共享对象的状态时&#xff0c;除了原子性外&#xff0c;其他问题也会发挥作用。 其中之一是可见性。 关键事实是&#xff0c;如果没有同步&#xff0c;则不能保证指令按照它们在源代码中出现的顺序执行。 这不会影响单线程程序中的结果&#xff0c;但是&#…

让element-ui的输入框聚焦的4种方式

方法一、绑定ref 方法二、通过自定义事件中的事件对象 $event&#xff0c;找到input 方法三、使用自定义指令 方法四、使用原生input 方法一、绑定ref——参考yiyueqinghui <el-input v-model"form.name" ref"name"></el-input> this.$refs.n…

控制台打印三角形、菱形

一、 打印三角形 要求&#xff1a; 打印如下类似的三角形 * *** ***** ******* 效果&#xff1a;思路&#xff1a; &#xff08;1&#xff09; 空格每往下一层少一个。 &#xff08;2&#xff09; *是奇数数列&#xff0c;&#xff08;i – 1&#xff09;* 2 1 核心代码&#…

《React Native 精解与实战》书籍连载「React Native 网络请求与列表绑定」

此文是我的出版书籍《React Native 精解与实战》连载分享&#xff0c;此书由机械工业出版社出版&#xff0c;书中详解了 React Native 框架底层原理、React Native 组件布局、组件与 API 的介绍与代码实战&#xff0c;以及 React Native 与 iOS、Android 平台的混合开发底层原理…

在CloudBees上开发,测试和部署独立应用程序

CloudBees是一个云平台&#xff0c;为您的应用程序提供存储库&#xff0c;CI服务&#xff08;Jenkins&#xff09;和服务器。 因此&#xff0c;您需要开发&#xff0c;测试和部署所有内容。 有很多选项&#xff0c;例如存储库可以是Git或SVN&#xff0c;对于服务器&#xff0c;…

第五次实验

数组和指针 1. 设N个整数有序&#xff08;由小到大&#xff09;存放在一维数组中。编写函数binarySearch(),实现使用二分查找算法在一维数组中 查找特定整数item。如果找到&#xff0c;返回item在数组元素中的下标&#xff1b;如果item不在数组中&#xff0c;则返回-1。 实现方…

《React Native 精解与实战》书籍连载「Node.js 简介与 React Native 开发环境配置」

此文是我的出版书籍《React Native 精解与实战》连载分享&#xff0c;此书由机械工业出版社出版&#xff0c;书中详解了 React Native 框架底层原理、React Native 组件布局、组件与 API 的介绍与代码实战&#xff0c;以及 React Native 与 iOS、Android 平台的混合开发底层原理…

React.js 入门与实战课程思维导图

原文发表于我的技术博客 我在慕课网的「React.js 入门与实战之开发适配PC端及移动端新闻头条平台」课程已经上线了&#xff0c;在这里分享了课程中的思维导图&#xff0c;供大家参考。 原文发表于我的技术博客 此导图为课程中整理的重要知识点以及大纲导图&#xff0c;供大家学…

Webpack 2 视频教程 002 - NodeJS 安装与配置

原文发表于我的技术博客 这是我免费发布的高质量超清「Webpack 2 视频教程」。 Webpack 作为目前前端开发必备的框架&#xff0c;Webpack 发布了 2.0 版本&#xff0c;此视频就是基于 2.0 的版本讲解的。 这个基本就是目前国内最好的 Webpack 2.0 最好的学习视频了&#xff0c;…

vue+elementUI 添加多个可以全选的多选框

elementUI-checkbox官网&#xff1a;https://element.eleme.cn/#/zh-CN/component/checkbox 一、要做上面这种效果&#xff0c;首先要了解全选框中indeterminate 状态和v-model的变量的关系 参考 -Dayer-&#xff1a; <el-checkbox :indeterminate"isIndeterminate&…

WMI in C#[强类型操作]

C# 进行WMI操作的内容封装在System.Management.dll中&#xff0c;具体的在MSDN有详细描述&#xff0c;默认是用[“**’]去读取和设置属性等。 此处介绍一个自动生成强类型的包装软件&#xff0c;这样处理起来就简单了很多&#xff0c;不用在反复的查看参数等信息了。 1、 使用W…

2019年ipa发布苹果应用商店审核指南

https://baijiahao.baidu.com/s?id1623886553597961077&wfrspider&forpc ipa 发布审核指南 说明&#xff1a; 本指南为初版&#xff0c;旨在帮助非技术人员快速了解苹果ipa发布审核流程非技术的审核专员发布审核只需处理 &#xff08;五&#xff09;、iTunes connect …

GridView生成序号

一个经常碰到的情况&#xff1a;GridView需要添加一个序号列&#xff0c;并且从1开始自动编号。而数据库中的ID往往是不连续的&#xff08;会有记录被删除的情况&#xff09;&#xff0c;我们无法绑定现有字段作为编号。因此我们需要手动给GridView编号。 思路&#xff1a;在Gr…

[前端优化]使用Microsoft Ajax Minifier对资源文件进行压缩优化

在前端优化中&#xff0c;js、css等文件的优化一般都是压缩的优化&#xff0c;进行合并、减小体积以达到减小请求的目的。 今天发现了一个集成在VS中的压缩插件&#xff0c;使得压缩变得比较快捷。 配置方法 首先需要去下载Microsoft Ajax Minifier&#xff0c;一路安装就可以&…

手把手教你用 elementUI 实现导航栏

elementUI导航栏官网 1. 安装 elementUI 2. 文件准备 3. 配置路由 4. 导航栏代码 一、安装 elementUI npm i element-ui -S&#xff1b; 在 main.js 中注册组件&#xff1a; import ElementUI from element-ui; import element-ui/lib/theme-chalk/index.css; Vue.use(Elem…

vuex+element 从后台获取数据写导航栏-菜单权限

主要用到 vuex、router.beforeEach、router.addRoutes()。vuex 的使用方法可以看我的另一篇博客&#xff1a;vue笔记&#xff08;四&#xff09;vuex。 顺便安利一个 在线视频转gif图。 因为第一次用到 router.addRoutes()&#xff0c;在做这个需求的时候遇到了很多问题&…

P3393 逃离僵尸岛 最短路dijkstra

题目描述 小a住的国家被僵尸侵略了&#xff01;小a打算逃离到该国唯一的国际空港逃出这个国家。 该国有N个城市&#xff0c;城市之间有道路相连。一共有M条双向道路。保证没有自环和重边。 K个城市已经被僵尸控制了&#xff0c;如果贸然闯入就会被感染TAT...所以不能进入。由其…

JavaFX技巧7:使用CSS颜色常量/派生颜色

在使用FlexCalendarFX时&#xff0c;我不得不定义一组颜色以可视化不同颜色的不同日历的控件。 每个日历不仅提供一种颜色&#xff0c;还提供几种&#xff1a;用于取消选择/选定/悬停状态的背景和文本颜色。 颜色曾在多个地方使用过&#xff0c;但为了简洁起见&#xff0c;我仅…