This article has authorized the public number: Yang (hongyangandroid) in the public platform original debut.
Extra! I am participating in the CSDN2016 Year Blog Star Award, please give me a vote.
Here is my address: http://blog.csdn.net/vote/candidate.html?username=qibin0506
Of course, there are Yang: http://blog.csdn.net/vote/candidate.html?username=lmj623565791.
Note: Every day you can vote Wow, no one has 10 tickets per day ~ ~ ~
Well, the following into today's theme, the first few months have been fortunate to participate in the CSDN organization of the MDCC Mobile Developers Conference, one day down my biggest harvest is to understand the modular development, back then I have been thinking about some of the advantages of modularity, do not say anything else, provide a pluggable way of development is enough for us to be excited for a while ~ Then I began to try some small demo, found that in the modular development of the biggest problem is the communication between components, such as: In the modular architecture, the mall and personal Center is two separate modules, in the development phase, the personal center how to jump to a page of the mall to do? Here we need to introduce the concept of a route. Web development is known that in most Web frameworks URL routing is also a very important part of the framework, if you are not very clear about the concept of routing, you can first look at my this go web development of the URL routing design to understand the concept of the next route, A little explanation here is that routing is the role of forwarding.
A picture to understand the role of the route, because I do not have a local UML tools, the new is still in the download ... 900m+, I'm a little overwhelmed by the speed. So I chose Kolourpaint to manually draw an enchanted picture first to experience.
The motivation to implement a route yourself
What about the development of our Android? If we modular the project, the two components to communicate or jump, we generally build intent way is no longer used, very simple, because in module A is not found in module B in the Class C, which requires us to customize the routing rules, around a bend to jump, To be blunt is to give your class an alias, which we use not to refer to. In fact, when I was ready to implement a route myself, I was Google some solutions, these programs can be broadly divided into two kinds.
- Fully implement the route itself, fully encapsulate the jump parameters
- Use implicit intent to jump
For both of these ways I summed up, personally think the first way to encapsulate too much, even some of the framework is restful like, such a package is the cost of learning is too high, second, the old project is too cumbersome to change. What about the second way? The use of implicit intent is a good choice, and Android native support, which is also a choice when trying to modular development, but this way only support activity, Service, Broadcastreceiver, scalability is too poor. On top of all the factors, I decided to implement a route myself, referring to the limitations above, our route has a few 2 features.
- Easy to get started, the goal is to be able to implement activity, Service, Broadcastreceiver calls with the difference of native mode line code.
- Extensibility is strong, developers can add their own routing implementation, not limited to activity, service, Broadcastreceiver.
Try
Before understanding the specific implementation code, we first to understand how the new route is used, is not in line with the above two points, first we first set up three Moduler, respectively, Shell app, mall module Shoplib, BBS module BBSLIB. The app module is our shell, we need to use the app module to pack, and the app is also dependent on shoplib and bbslib, so we can in the app's application to do the route registration.
public class app extends application { @Override public void oncreate () {super . OnCreate (); Setuprouter (); } private void Setuprouter () {router.router (activityrule.activity_scheme + "Shop.main" , S Hopactivity.class); Router.router (activityrule.activity_scheme + "Bbs.main" , Bbsactivity.class); }}
There are two routes registered here, respectively, the shopactivity of the mall module and the bbsactivity of the BBS module, they are registered through the Router
static method method of the class router
, two parameters, the first parameter is the routing address (also can be understood as an alias), The class that corresponds to the second parameter. Registration is over, then the next is how to use, we take a look at the mall module how to jump BBS module it.
Public class shopactivity extends appcompatactivity { @Override protected void onCreate(@Nullable Bundle savedinstancestate) {Super. OnCreate (Savedinstancestate); TextView TV =NewTextView ( This); Tv.settextsize ( -); Tv.settext ("Shop!!!"); Setcontentview (TV); Tv.setonclicklistener (NewView.onclicklistener () {@Override Public void OnClick(View v) {Intent it = Router.invoke (shopactivity. This, Activityrule.activity_scheme +"Bbs.main"); StartActivity (IT); } }); }}
The main code is in the click event, we call the Router.invoke
method, the first parameter is the current activity, the second parameter is our previous registration of the route, here is very good understanding, the key is to see its return value, here directly return a intent, this is the best ~ Returning intent means that the code below is no different from the way we use native methods! This is in line with the simple purpose we have mentioned above.
As for the 2nd goal, High scalability , you can implement the Rule
interface custom route rule, and then call Router.addRule(String scheme, Rule rule)
the method to register the routing rules. The rule interface is defined as follows,
/** * Routing Rules Interface <br/> * Created by Qibin on 2016/10/8. */ public interface rule <t , v > { /** * Add route * @param patter N Route URI * @param Klass Route class */ void
Router (String pattern, class<t> Klass); /** * route call * @param ctx Context * @param pattern route uri * @return {@code V} returns the corresponding return value */ V Invoke (Context ctx, String pattern);}
To explain, first of all, the two paradigms of the rule interface, the first T is the type of route we registered, such as the activity type used earlier, and the second V is the invoke
return value type of the method, such as the intent type used earlier. As for the custom code, here I am. Not provided demo~~~ everyone can try to customize it.
Routing Implementation Code
Next we begin to enter the implementation code-before we come to the code, or a diagram to understand the Router
structure of the next.
With the above picture, we look at the code, first we look at the router class, after all, we are in use when we are dealing with router.
/** * Usage: <br/> * <pre> * Step 1. Call the Router.router method to add a route * Step 2. Call the Router.invoke method to route according to the pattern call * < ;/pre> * Created by Qibin on 2016/10/9. */ Public class Router { /** * Add custom route rules * @param Scheme Routing scheme * @param Rule Routing rules * @return {@cod E routerinternal} Router real call class * / Public StaticRouterinternalAddRule(String scheme, rule rule) {routerinternal router = routerinternal.get (); Router.addrule (scheme, rule);returnRouter }/** * Add route * @param pattern Route uri * @param Klass Route class * @return {@co De routerinternal} router Real call class * / Public Static<T> routerinternalRouter(String pattern, class<t> Klass) {returnRouterinternal.get (). Router (pattern, Klass); }/** * Route call * @param ctx Context * @param pattern Route uri * @return {@code V} Returns the corresponding return value * / Public Static<V> VInvoke(Context ctx, String pattern) {returnRouterinternal.get (). Invoke (ctx, pattern); }}
Ha, router code is very simple, mainly to play a similar role as a static agent, the main code is still in RouterInternal
, then look at RouterInternal
the structure of it.
Public class routerinternal { Private StaticRouterinternal sinstance;/** scheme-> Routing rules * / PrivateHashmap<string, rule> mrules;Private routerinternal() {mrules =NewHashmap<> (); Initdefaultrouter (); }/** * Add the default activity,service,receiver route * / Private void Initdefaultrouter() {AddRule (Activityrule.activity_scheme,NewActivityrule ()); AddRule (Servicerule.service_scheme,NewServicerule ()); AddRule (Receiverrule.receiver_scheme,NewReceiverrule ()); }/*package * * StaticRouterinternal get () {if(Sinstance = =NULL) {synchronized(Routerinternal.class) {if(Sinstance = =NULL) {sinstance =NewRouterinternal (); } } }returnSinstance; }}
First RouterInternal
is a singleton, a mRules
variable to save our routing rules, in the construction we registered three default routing rules, these three routing rules do not want to know is activity, service and Broadcastreceiver. Next look at the other methods.
/** * 添加自定义路由规则 * @param scheme 路由scheme * @param rule 路由规则 * @return {@code RouterInternal} Router真实调用类 */publicfinaladdRule(String scheme, Rule rule) { mRules.put(scheme, rule); returnthis;}
addRule
The method is to add the implementation of the routing rule, and here we add it directly to mRules
this HashMap
.
privategetRule(String pattern) { HashMap<String, Rule> rules = mRules; Set<String> keySet = rules.keySet(); null; for (String scheme : keySet) { if (pattern.startsWith(scheme)) { rule = rules.get(scheme); break; } } return rule;}
getRule
The role is to pattern
get the rules according to, this is a private method, so in the use of the time do not need to care, its principle is very simple, is based on your pattern
match scheme
to get the corresponding Rule
.
/** * Add route * @param pattern route uri * @param Klass Route class * @return {@code routerinternal} router Real call class */ public final <T> routerinternal router ( String pattern, class<t> Klass) {rule<t,?> Rule = Getrule (pattern); if (rule = = null ) {throw new notrouteexception (" unknown ", pattern); } rule.router (pattern, Klass); return this ;}
This router
method is that we add the implementation of the route, we first based on the URI of the route to get the corresponding Rule
, and then call the Rule
router
method, as Rule.router
to how the method is implemented, we later see ~
/** * 路由调用 * @param ctx Context * @param pattern 路由uri * @return {@code V} 返回对应的返回值 *//*package*/final <V> V invoke(Context ctx, String pattern) { Rule<?, V> rule = getRule(pattern); ifnull) { thrownew NotRouteException("unknown", pattern); } return rule.invoke(ctx, pattern);}
invoke
The method is the code that we execute when we call, the return value T
is the type specified in the returned Rule
paradigm, for example, the preceding Intent
.
In the code, we find that RouterInternal
it is actually a management Rule
class, the specific call is implemented in each, as Rule
mentioned above, Rule
is an interface, it has two paradigms, corresponding to the invocation of the invoke
return value type and the type of the class we want to route. Let's take a look at how the default number of routing rules is implemented.
For the activity, Service, Broadcastreceiver call, summed up, they are actually the type of return Intent
, so we can first build a specified return value is Intent
the base type.
/** * Returns the base class for intent routing rules <br/> * Created by Qibin on 2016/10/9. */ Public Abstract class baseintentrule<t> implements Rule<t, Intent> { PrivateHashmap<string, class<t>> mintentrules; Public Baseintentrule() {mintentrules =NewHashmap<> (); }/** * {@inheritDoc} */ @Override Public void Router(String pattern, class<t> Klass) {Mintentrules.put (pattern, Klass); }/** * {@inheritDoc} */ @Override PublicIntentInvoke(Context ctx, String pattern) {Class<t> Klass = Mintentrules.get (pattern);if(Klass = =NULL{throwexception (pattern);}return NewIntent (CTX, Klass); }/** * Throws an exception when no routing rule is found * @param pattern route pattern */ Public Abstract void ThrowException(String pattern);}
router
Not much to say, or to Map
add a key value pair, the invoke
method, we pass the parameter pattern
from the mIntentRules
target class, and then build a Intent
return, the last throwException
is an abstract method, used to call the router
class without the exception to throw the ~, you can find , most implementations are implemented here, inheriting this for the activity, BaseIntentRule
and specifying the type of class to be routed is activity, and the implementation throwException
method is available.
/** * Activity Routing rules <br/> * Created by Qibin on 2016/10/8. */ Public class activityrule extends baseintentrule<Activity> { /** Activity Routing scheme*/ Public Static FinalString Activity_scheme ="activity://";/** * {@inheritDoc} */ @Override Public void ThrowException(String pattern) {Throw NewActivitynotrouteexception (pattern); }}
ActivityRule
First inherit BaseIntentRule
and specify the paradigm is, the Activity
implementation of the throwException
method is also very simple, is to throw an ActivityNotRouteException
exception, for this exception, we can find in the last source download section of the article to read ActivityRule
the implementation, in fact, the other two default Rule
implementations are the same ~ Everyone is going to see the code yourself.
In fact, the implementation of a route is very simple, the principle is to give us the class to be routed to define an alias, and then call the place through the alias to invoke. And in the package as far as possible to meet the current user's habits, not too much encapsulation and ignore the users feel.
Well, this article is here, the code in the article can go to https://github.com/qibin0506/Module2Module a modular development of the small demo to find ~
Android Routing implementation